Эффективная загрузка изображений: Полное руководство для веб-разработчиков
В этом посте мы рассмотрим, как реализовать функциональность загрузки файлов в веб-приложении с использованием JavaScript, HTML и PHP. Мы сосредоточимся на загрузке изображения, которое будет служить ключевым изображением для новости. В дальнейшем мы также обсудим редактирование загруженного изображения и добавление дополнительных файлов.
Часть 1: HTML-разметка
Первым делом необходимо создать HTML-шаблон для загрузки изображений. Вот пример кода, который использует Bootstrap для создания пользовательского интерфейса:
<div class="text-field col-sm-4">
<div class="card file-upload">
<div class="card-header">
<h3 class="card-title">Ключевое изображение</h3>
</div>
<div class="card-body">
<input type="file" name="base" class="file-input" accept="image/*" />
<label class="file-label">Перетащите файл сюда или нажмите для выбора</label>
<div class="file-list"></div>
</div>
</div>
</div>
Этот шаблон содержит заголовок, область для загрузки файлов и список для отображения загруженных изображений.
Часть 2: JavaScript для загрузки файлов
Теперь добавим необходимый JavaScript-код для обработки загрузки файлов. Этот код позволяет пользователю перетаскивать изображения или выбирать их через стандартное поле ввода. Он также обеспечивает отображение загруженных изображений и управление их удалением.
// Работа с изображениями
const fileUploads = document.querySelectorAll('.file-upload');
fileUploads.forEach(upload => {
const fileInput = upload.querySelector('.file-input');
const uploadLabel = upload.querySelector('.file-label');
const fileList = upload.querySelector('.file-list');
const dataTransfer = new DataTransfer(); // Создаем новый объект DataTransfer
// Создаем элемент прелоадера
const preloader = document.createElement('div');
preloader.className = 'preloader';
preloader.style.display = 'none'; // Скрываем прелоадер по умолчанию
upload.appendChild(preloader); // Добавляем прелоадер в DOM
// Обработчик события перетаскивания
upload.addEventListener('dragover', (event) => {
event.preventDefault();
});
// Обработчик события сброса
upload.addEventListener('drop', (event) => {
event.preventDefault();
const files = event.dataTransfer.files;
addFilesToList(files);
});
// Обработчик выбора файлов
fileInput.addEventListener('change', (event) => {
const files = event.target.files;
addFilesToList(files);
});
// Функция для добавления файлов в список
function addFilesToList(files) {
preloader.style.display = 'block'; // Показываем прелоадер при начале загрузки
// Очищаем список и DataTransfer перед добавлением новых файлов
fileList.innerHTML = '';
dataTransfer.items.clear();
for (let i = 0; i < files.length; i++) {
const file = files[i];
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
// Проверяем, является ли файл изображением
if (file.type.startsWith('image/')) {
const img = document.createElement('img');
img.src = URL.createObjectURL(file);
img.className = 'file-preview';
fileItem.appendChild(img);
const fileName = document.createElement('div');
fileName.className = 'file-name';
fileName.textContent = file.name;
fileItem.appendChild(fileName);
const removeButton = document.createElement('button');
removeButton.textContent = 'Удалить';
removeButton.className = 'remove-button';
removeButton.addEventListener('click', () => {
fileItem.remove();
// Обновляем DataTransfer и input.files
});
fileItem.appendChild(removeButton); // Разместим кнопку удаления
dataTransfer.items.add(file);
fileList.appendChild(fileItem); // Добавляем файл в список
}
}
fileInput.files = dataTransfer.files; // Обновляем input файлов
// Скрыть input и текст при загрузке хотя бы одного файла
if (fileList.children.length > 0) {
fileInput.classList.add('hidden');
uploadLabel.style.display = 'none'; // Скрыть текст загрузки
}
// Скрыть прелоадер
setTimeout(() => {
preloader.style.display = 'none';
const images = fileList.querySelectorAll('.file-preview');
images.forEach(image => {
image.classList.add('visible'); // Добавляем класс для плавного появления
});
}, 1000);
}
});
В этом коде мы создали функциональность, чтобы загрузить изображение, показывать его превью и показывать кнопку Удалить.
Часть 3: Серверная часть на PHP
Теперь давайте рассмотрим серверный код на PHP, который будет обрабатывать загрузку фоновых файлов. Мы предполагаем, что у вас есть контроллер (Controller), обрабатывающий запросы.
public function addAction()
{
if (isset($_POST['add-news'])) {
$news = new Blog(); // Создает новый объект блога
$data = $_POST; // Получаем все данные из POST-запроса
$news->load($data); // Загружаем данные в объект
// Устанавливаем атрибуты на основе состояния чекбоксов
$news->attributes['trending_post'] = isset($data['trending_post']) ? 1 : 0;
$news->attributes['visibility'] = isset($data['visibility']) ? 1 : 0;
// Валидация данных
if (!$news->validate($data)) {
$_SESSION['response_admin'] = $data; // Сохранение данных в сессии
redirect(); // Перенаправление
}
$sizes = [
[870, 475, 'blog'], // Размер для блога
[360, 197, 'gallery'], // Размер для галереи
[85, 47, 'small'], // Размер для маленького изображения
];
if ($getNewsID = $news->save('news')) {
// Загружаем изображения с передачей ID новости
$uploadedFiles = $this->uploadImg(['base'], $sizes, WWW . '/assets/img/', $getNewsID, $data);
// Обработка загруженных изображений по типу
// ...
}
}
}
uploadImg - это метод, который обрабатывает загрузку изображений. Он проверяет ошибки, проверяет размер, объединяет и сохраняет изображения.
Теперь перейдем к важному методу, который обрабатывает загрузку изображений.
public function uploadImg(array $inputNames, array $sizes, string $uploadDir, int $newsID = null, array $data = [], $duplicate = TRUE): array
{
$uploadedFiles = [];
$_SESSION['response']['error'] = '';
// Проверка настройки file_uploads
if (!ini_get('file_uploads')) {
$_SESSION['response']['error'] .= 'Загрузка файлов отключена на сервере.';
return $uploadedFiles;
}
$maxFileSize = ini_get('upload_max_filesize');
$postMaxSize = ini_get('post_max_size');
if ($maxFileSize === FALSE || $postMaxSize === FALSE) {
$_SESSION['response']['error'] .= 'Не удалось получить настройки максимального размера загружаемого файла.';
return $uploadedFiles;
}
$maxFileSizeBytes = $this->convertToBytes($maxFileSize);
$postMaxSizeBytes = $this->convertToBytes($postMaxSize);
// Генерация имени файла один раз
$new_name = null; // Изначально сбрасываем переменную
foreach ($inputNames as $inputName) {
if (isset($_FILES[$inputName]) && !empty($_FILES[$inputName]['name'])) {
// Генерация имени файла
$ext = strtolower(pathinfo($_FILES[$inputName]['name'], PATHINFO_EXTENSION));
$slug = AppModel::str2url($data['alias'] ?: $data['heading']);
$new_name = "news_{$newsID}_{$slug}.{$ext}"; // Генерация имени файла только один раз
break; // Выходим из цикла, так как имя файла сгенерировано
}
}
if (empty($new_name)) {
// Если ни один файл не загружен, просто выходим
$_SESSION['response']['error'] .= 'Нет загруженных файлов.';
return $uploadedFiles;
}
// Обработка каждого файла
foreach ($inputNames as $index => $inputName) {
if (!isset($_FILES[$inputName])) {
continue; // Пропустим, если данные файла не существуют.
}
$fileData = $_FILES[$inputName];
if (!empty($fileData['name']) && is_uploaded_file($fileData['tmp_name'])) {
// Проверяем наличие ошибок загрузки
if ($fileData['error'] !== UPLOAD_ERR_OK) {
$errorMessage = $this->getUploadErrorMessage($fileData['error']);
$_SESSION['response']['error'] .= "Ошибка при загрузке файла {$fileData['name']}: {$errorMessage}";
continue;
}
// Проверка размера файла
if ($fileData['size'] > $maxFileSizeBytes || $fileData['size'] > $postMaxSizeBytes) {
$_SESSION['response']['error'] .= "Размер загружаемого файла для {$inputName} превышает допустимый предел.";
continue;
}
// Указываем директорию для загрузки
$targetDir = $uploadDir . $sizes[$index][2] . '/';
if (!is_dir($targetDir)) {
mkdir($targetDir, 0777, TRUE); // Создаем директорию, если она не существует
}
// Полный путь к файлу
$targetFile = $targetDir . $new_name;
// Перемещение загруженного файла
if (!move_uploaded_file($fileData['tmp_name'], $targetFile)) {
$_SESSION['response']['error'] .= "Не удалось загрузить файл {$fileData['name']} в {$sizes[$index][2]}.";
continue;
}
// Изменение размера изображения
$image = new ResizeImage();
$image->load($targetFile);
$image->resize($sizes[$index][0], $sizes[$index][1]);
$image->save($targetFile);
$uploadedFiles[$sizes[$index][2]][] = $new_name; // Сохраняем имя загруженного файла
} else if ($duplicate) {
// Если файла нет, используем базовую картинку
$this->copyBaseImageToTarget($uploadDir, $inputName, $sizes[$index], $new_name);
}
}
return $uploadedFiles;
}
Метод getUploadErrorMessage
// Функция для получения текстового описания ошибки загрузки
private function getUploadErrorMessage($errorCode)
{
switch ($errorCode) {
// Ошибка не произошла, файл успешно загружен
case UPLOAD_ERR_OK:
return 'Нет ошибки, файл был загружен успешно.';
// Размер загруженного файла превышает максимальный размер, установленный в конфигурации PHP
case UPLOAD_ERR_INI_SIZE:
return 'Размер загруженного файла превышает директиву upload_max_filesize в php.ini.';
// Размер загруженного файла превышает ограничение, указанное в HTML-форме
case UPLOAD_ERR_FORM_SIZE:
return 'Размер загруженного файла превышает директиву MAX_FILE_SIZE, указанную в HTML-форме.';
// Файл был загружен только частично
case UPLOAD_ERR_PARTIAL:
return 'Файл был только частично загружен.';
// Файл не был загружен
case UPLOAD_ERR_NO_FILE:
return 'Файл не был загружен.';
// Временная папка для загрузки отсутствует
case UPLOAD_ERR_NO_TMP_DIR:
return 'Нет временной папки на сервере.';
// Не удалось записать файл на диск
case UPLOAD_ERR_CANT_WRITE:
return 'Не удалось записать файл на диск.';
// Загрузка файла была остановлена из-за расширения
case UPLOAD_ERR_EXTENSION:
return 'Загрузка файла остановлена из-за расширения.';
// Неизвестная ошибка
default:
return 'Неизвестная ошибка при загрузке.';
}
}Метод copyBaseImageToTarget
private function copyBaseImageToTarget($uploadDir, $inputName, $size, $new_name)
{
// Проверяем, относится ли текущий inputName к 'gallery' или 'small'
if (in_array($inputName, ['gallery', 'small'])) {
// Формируем путь к целевой директории
$targetDir = $uploadDir . $size[2] . '/';
// Создаем каталог, если он не существует
if (!is_dir($targetDir)) {
mkdir($targetDir, 0777, true); // Создаем директорию с правами 0777
}
// Полный путь к файлу, который будем копировать
$targetFile = $targetDir . $new_name;
$sourceFile = $uploadDir . 'blog/' . $new_name; // Путь к исходному файлу
// Проверка существования исходного файла
if (!file_exists($sourceFile)) {
$_SESSION['response']['error'] .= "<li>Базовый файл не найден для копирования в {$size[2]}.</li>";
return; // Выход из функции, если файл не существует
}
// Проверка, является ли исходный путь файловой системой
if (!is_file($sourceFile)) {
$_SESSION['response']['error'] .= "<li>Исходный путь {$sourceFile} не является файлом.</li>";
return; // Выход, если путь к файлу не является файлом
}
// Копируем базовую картинку в нужный каталог
if (!copy($sourceFile, $targetFile)) {
$_SESSION['response']['error'] .= "<li>Не удалось скопировать базовый файл в {$size[2]}.</li>";
return; // Выход, если копирование не удалось
} else {
// Пытаемся изменить размер изображения
$image = new ResizeImage();
$image->load($targetFile);
$image->resize($size[0], $size[1]);
$image->save($targetFile); // Сохраняем измененное изображение
}
}
}Метод convertToBytes
// Функция для преобразования размера файла в байты
private function convertToBytes($size)
{
$unit = strtolower(substr($size, -1)); // Получаем единицу измерения (G, M, K)
$value = (int)$size; // Преобразуем значение размера в целое число
// Преобразование значения в байты, в зависимости от единицы измерения
switch ($unit) {
case 'g':
$value *= 1024; // Гигабайты в мегабайты
// no break
case 'm':
$value *= 1024; // Мегабайты в килобайты
// no break
case 'k':
$value *= 1024; // Килобайты в байты
// no break
}
return $value; // Возвращаем значение в байтах
}Этот код мы используем в своем блоге! В коде мы используем целый ряд функций для обработки ошибок, копирования изображений и преобразования размеров файлов. Комментарии помогают объяснить, что делает каждая часть кода, а также зачем нужны проверки и действия, когда дело касается работы с файлами. Если у вас есть дополнительные предложения по комментариям или другие подходы, дайте знать!
Заключение
В этом посте мы рассматривали процесс загрузки ключевых изображений в новости. Мы создали интерфейс для загрузки, написали JavaScript для управления файловым вводом и подготовили серверный код для сохранения изображений.
В следующем посте мы будем редактировать загруженные изображения и добавлять дополнительные файлы. Если у вас есть вопросы или пожелания, оставляйте их в комментариях!
Добавить комментарий или задать вопрос ツ
Комментариев нет