Загрузка файла(ов) на сервер из формы средствами PHP
Сегодня загрузка файлов является практически неотъемлемым атрибутом современного web приложения. В данной статье речь пойдёт о том, как же загрузить файл(ы) на сервер с помощью PHP.
Настройка php.ini
[Resource Limits] ; Максимальное время выполнения скрипта в секундах max_execution_time = 60 ; Максимальное потребление памяти одним скриптом memory_limit = 64M [Data Handling] ; Максимально допустимый размер данных отправляемых методом POST post_max_size = 5M [File Uploads] ; Разрешение на загрузку файлов file_uploads = On ; Папка для хранения файлов во время загрузки upload_tmp_dir = home/user/temp ; Максимальный размер загружаемого файла upload_max_filesize = 5M ; Максимально разрешённое количество одновременно загружаемых файлов max_file_uploads = 10
Конфигурационный файл php.ini необходимо настраивать согласно бизнес-логики проекта! Например, мы планируем загружать не более десяти файлов до 2 Мбайт, а это значит нам понадобиться ~20 Мбайт памяти.
Загрузка одного файла на сервер из формы
Для начала разберём механизм загрузки одной картинки на сервер. Для загрузки картинки с компьютера пользователя необходимо с помощью HTML-формы отправить нужный (выбранный) файл PHP-скрипту upload.php методом POST и указать способ кодирования данных enctype=»multipart/form-data» (в данном случае данные не кодируются и это значение применяется только для отправки бинарных файлов).
После отправки файла PHP-скрипту upload.php его можно перехватить с помощью суперглобальной переменной $_FILES с таким же именем, которая в массиве содержит информацию о файле (в нашем случае image ):
Array ( [name] => image.jpg // оригинальное имя файла [type] => image/jpeg // MIME-тип файла [tmp_name] => home\user\temp\phpD07E.tmp // бинарный файл [error] => 0 // код ошибки [size] => 17170 // размер файла в байтах )
Не всем данным из $_FILES можно доверять: MIME-тип и размер файла можно подделать, т. к. они формируются из HTTP-ответа, а расширению в имени файла не стоит доверять в силу того, что за ним может скрываться совершенно другой файл. Тем не менее, дальше нам нужно проверить корректно ли загрузился наш файл и загрузился ли он вообще. Для этого необходимо проверить ошибки в $_FILES[‘image’][‘error’] и удостовериться, что файл загружен методом POST с помощью функции is_uploaded_file() . Если что-то идёт не по плану, значит выводим ошибку на экран:
// Если в $_FILES существует "image" и она не NULL if (isset($_FILES['image'])) < $image = $_FILES['image']; // Получаем нужные элементы массива "image" $fileTmpName = $_FILES['image']['tmp_name']; $errorCode = $_FILES['image']['error']; // Проверим на ошибки if ($errorCode !== UPLOAD_ERR_OK || !is_uploaded_file($fileTmpName)) < // Массив с названиями ошибок $errorMessages = [ UPLOAD_ERR_INI_SIZE =>'Размер файла превысил значение upload_max_filesize в конфигурации PHP.', UPLOAD_ERR_FORM_SIZE => 'Размер загружаемого файла превысил значение MAX_FILE_SIZE в HTML-форме.', UPLOAD_ERR_PARTIAL => 'Загружаемый файл был получен только частично.', UPLOAD_ERR_NO_FILE => 'Файл не был загружен.', UPLOAD_ERR_NO_TMP_DIR => 'Отсутствует временная папка.', UPLOAD_ERR_CANT_WRITE => 'Не удалось записать файл на диск.', UPLOAD_ERR_EXTENSION => 'PHP-расширение остановило загрузку файла.', ]; // Зададим неизвестную ошибку $unknownMessage = 'При загрузке файла произошла неизвестная ошибка.'; // Если в массиве нет кода ошибки, скажем, что ошибка неизвестна $outputMessage = isset($errorMessages[$errorCode]) ? $errorMessages[$errorCode] : $unknownMessage; // Выведем название ошибки die($outputMessage); > else < echo 'Ошибок нет.'; >>;
Для того, чтобы «редиска» не загрузил вредоносный код, встроенный в изображение, нельзя доверять функции getimagesize() , которая также возвращает MIME-тип. Функция ожидает, что первый аргумент является ссылкой на корректный файл изображения. Определить настоящий MIME-тип картинки можно через расширение FileInfo . Код ниже проверит наличие ключевого слова image в типе нашего загружаемого файла и если его не окажется, выдаст ошибку:
// Создадим ресурс FileInfo $fi = finfo_open(FILEINFO_MIME_TYPE); // Получим MIME-тип $mime = (string) finfo_file($fi, $fileTmpName); // Проверим ключевое слово image (image/jpeg, image/png и т. д.) if (strpos($mime, 'image') === false) die('Можно загружать только изображения.');
Таким образом, при необходимости, делаем проверку и на другие MIME-типы. Например, для zip архивов проверка будет такая:
// Проверим ключевое слово zip (application/zip) if (strpos($mime, 'zip') === false) die('Можно загружать только архивы ZIP.');
На данном этапе мы уже можем загружать абсолютно любые картинки на наш сервер, прошедшие проверку на MIME-тип, но для загрузки изображений по определённым характеристикам нам необходимо валидировать их с помощью функции getimagesize() , которой отдадим сам бинарный файл $_FILES[‘image’][‘tmp_name’] . В результате мы получим массив из элементов:
// Результат функции запишем в переменную $image = getimagesize($fileTmpName); var_dump($image);die; // Результат Array ( [0] => 1280 // ширина [1] => 768 // высота [2] => 2 // тип [3] => width="1280" height="768" // атрибуты для HTML [bits] => 8 // глубина цвета [channels] => 3 // цветовая модель [mime] => image/jpeg // MIME-тип )
Для дальнейшей валидации изображения и работы над ним нам необходимо знать только 3 значения: ширину, высоту и размер файла (для вычисления размера применим функцию filesize() для бинарного файла из временной папки).
// Результат функции запишем в переменную $image = getimagesize($fileTmpName); // Зададим ограничения для картинок $limitBytes = 1024 * 1024 * 5; $limitWidth = 1280; $limitHeight = 768; // Проверим нужные параметры if (filesize($fileTmpName) > $limitBytes) die('Размер изображения не должен превышать 5 Мбайт.'); if ($image[1] > $limitHeight) die('Высота изображения не должна превышать 768 точек.'); if ($image[0] > $limitWidth) die('Ширина изображения не должна превышать 1280 точек.');
После всех проверок мы можем с уверенностью переместить наш загружаемый файл в какую-нибудь директорию с картинками. Делать лучше это через функцию move_uploaded_file(), которая работает в безопасном режиме. Перед перемещением файла нельзя забыть сгенерировать случайное имя и расширение из типа изображения для нашего файла. Вот так это выглядит:
// Сгенерируем новое имя файла на основе MD5-хеша $name = md5_file($fileTmpName); // Сгенерируем расширение файла на основе типа картинки $extension = image_type_to_extension($image[2]); // Сократим .jpeg до .jpg $format = str_replace('jpeg', 'jpg', $extension); // Переместим картинку с новым именем и расширением в папку /upload if (!move_uploaded_file($fileTmpName, __DIR__ . '/upload/' . $name . $format)) < die('При записи изображения на диск произошла ошибка.'); >echo 'Картинка успешно загружена!';
Вместо простого способа генерации имени файла на основе MD5-хеша можно пойти более продвинутым путём, а именно написать отдельную функцию, которая будет проверять уникальность названия картинки для того, чтобы случайно не перезаписать уже загруженный файл. Если такого названия ещё нет, функция сгенерирует его. Такая проблема появляется в больших проектах и с большим количеством картинок. Но всё же)
function getRandomFileName($path) < $path = $path ? $path . '/' : ''; do < $name = md5(microtime() . rand(0, 9999)); $file = $path . $name; >while (file_exists($file)); return $name; >
Генерация имени для картинки теперь будет такой:
// Сгенерируем новое имя файла через функцию getRandomFileName() $name = getRandomFileName($fileTmpName);
Мы реализовали простой, но в тоже время практичный (с точки зрения безопасности) механизм загрузки файла на сервер. Весь код целиком лежит здесь .
Загрузка нескольких файлов на сервер из формы
Разберём механизм загрузки нескольких изображений за один раз с локальной машины пользователя. Продолжим дальше работать с $_FILES . Наша новая HTML-форма будет немного отличаться от старой.
Как видно в конец имени поля выбора файла name=»images[]» добавились фигурные скобки и атрибут multiple , который разрешает браузеру выбрать несколько файлов. Все файлы снова загрузятся во временную папку, если не будет никаких ошибок в php.ini . Перехватить их можно в $_FILES , но на этот раз суперглобальная переменная будет иметь неудобную структуру для обработки данных в массиве. Решается эта задача небольшими манипуляциями с массивом:
// Изменим структуру $_FILES foreach($_FILES['images'] as $key => $value) < foreach($value as $k =>$v) < $_FILES['images'][$k][$key] = $v; >// Удалим старые ключи unset($_FILES['images'][$key]); > // Загружаем все картинки по порядку foreach ($_FILES['images'] as $k => $v) < // Загружаем по одному файлу $fileTmpName = $_FILES['images'][$k]['tmp_name']; $errorCode = $_FILES['images'][$k]['error']; // Проверим на ошибки // . >
Мы реализовали механизм загрузки нескольких файлов на сервер. Весь код целиком лежит здесь .
Как я могу выбрать и загрузить несколько файлов с помощью HTML и PHP, используя HTTP POST?
У меня есть опыт загрузки одного файла с помощью . Однако у меня возникают проблемы с загрузкой более одного файла за раз. Например, я хочу выбрать серию изображений и загрузить их на сервер все сразу. Было бы здорово использовать элемент управления вводом одного файла, если это возможно. Кто-нибудь знает, как этого добиться?
Ответ 1
Это возможно в HTML5. Пример (PHP 5.4):
if (isset($_FILES[‘my_file’]))
$myFile = $_FILES[‘my_file’];
$fileCount = count($myFile[«name»]);
for ($i = 0; $i < $fileCount; $i++)
?>
File #:
Имя:
Временный файл:
Тип:
Размер:
Ошибка:
>
>
?>
Это лишь набросок полностью рабочего ответа. Смотрите Руководство по PHP:
Обработка загрузки файлов для получения дополнительной информации о правильной и безопасной обработке загрузки файлов в PHP.
Ответ 2
Есть несколько вещей, которые необходимо сделать, чтобы создать загрузку нескольких файлов . Н а самом деле это довольно просто. Вам не нужно использовать Java, Ajax, Flash. Просто создайте обычную форму загрузки файлов, начиная с:
И тогда:
НЕ забывайте про скобки! В файле post_upload.php попробуйте сделать следующее:
Обратите внимание, что вы получите массив с данными tmp_name, что будет означать, что вы можете обращаться к каждому файлу с помощью третьей пары скобок с примером файла ‘number’:
$_FILES[‘file’][‘tmp_name’][0]
Вы можете использовать php count() для подсчета количества файлов, которые были выбраны.
Ответ 3
Полное решение для Firefox:
echo «Количество файлов для загрузки : «.count($_FILES[‘infile’][‘name’]).»
«;
$uploadDir = «images/»;
for ($i = 0; $i < count($_FILES['infile']['name']); $i++)
echo «File names : «.$_FILES[‘infile’][‘name’][$i].»
«;
$ext = substr(strrchr($_FILES[‘infile’][‘name’][$i], «.»), 1);
// генерируем случайное новое имя файла, чтобы избежать конфликта имен
$fPath = md5(rand() * time()) . «.$ext»;
echo «Путь к файлу : «.$_FILES[‘infile’][‘tmp_name’][$i].»
«;
$result = move_uploaded_file($_FILES[‘infile’][‘tmp_name’][$i], $uploadDir . $fPath);
if (strlen($ext) > 0)
echo «Загрузка произведена». $fPath .» успешно.
«;
>
>
echo «Загрузка завершена.
«;
?>
Ответ 4
Если вы хотите выбрать несколько файлов из диалогового окна выбора файлов, которое отображается при выборе пункта browse, то вам в основном не повезло. Вам нужно будет использовать Java-апплет или что-то подобное (я думаю, что есть один вариант, который использует небольшой flash-файл, я обновлю, если найду его). В настоящее время ввод одного файла позволяет выбрать только один файл. Если вы говорите об использовании ввода нескольких файлов, то не должно быть большой разницы с использованием одного. Существует один метод использования одной кнопки « browse » , который использует flash. Я никогда не использовал его лично, но читал о нем достаточно много. Думаю, это лучший вариант.
http://swfupload.org/
Ответ 5
Выберите изображение для загрузки:
Использование цикла FOR:
$file_dir = «uploads»;
if (isset($_POST[«submit»]))
for ($x = 0; $x < count($_FILES['file']['name']); $x++)
$file_name = $_FILES[‘file’][‘name’][$x];
$file_tmp = $_FILES[‘file’][‘tmp_name’][$x];
/* местоположение для сохранения файла */
$file_target = $file_dir . DIRECTORY_SEPARATOR . $file_name; /* DIRECTORY_SEPARATOR = / or \ */
if (move_uploaded_file($file_tmp, $file_target))
echo » был загружен.
«;
> else
echo «Ошибка, файл не был загружен .»;
>
>
>
?>
Использование цикла FOREACH:
$file_dir = «uploads»;
if (isset($_POST[«submit»]))
foreach ($_FILES[‘file’][‘name’] as $key => $value)
$file_name = $_FILES[‘file’][‘name’][$key];
$file_tmp = $_FILES[‘file’][‘tmp_name’][$key];
/* местоположение для сохранения файла */
$file_target = $file_dir . DIRECTORY_SEPARATOR . $file_name; /* DIRECTORY_SEPARATOR = / or \ */
if (move_uploaded_file($file_tmp, $file_target))
echo » был загружен.
«;
> else
echo «Ошибка, файл не был загружен .»;
>
>
>
?>
Мы будем очень благодарны
если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.