Статьи

Загрузка файлов с помощью PHP

Что общего между изображениями в онлайн-фотоальбоме, вложениями электронной почты в веб-почтовом клиенте и файлами данных, переданными в онлайн-приложение для пакетной обработки? Все они полагаются на возможность загружать файлы через Интернет из веб-браузера пользователя. Действительно, загрузка файлов является важной особенностью многих сайтов и веб-приложений, которые мы используем ежедневно. В этой статье я покажу вам, как добавить поддержку загрузки файлов на ваш сайт с использованием PHP.

Требования

Обработка загрузки файлов не сложна, но есть несколько мелких деталей, которые должны быть правильными, иначе загрузка не удастся. Во-первых, вы должны убедиться, что PHP настроен на загрузку. Проверьте файл php.ini и убедитесь, что директива file_uploads включена.

  file_uploads = On

Загруженные файлы сначала сохраняются во временном каталоге (не волнуйтесь… ваш PHP-скрипт может впоследствии переместить файлы в более постоянное место). По умолчанию начальным местоположением является временный каталог системы по умолчанию. Вы можете указать другой каталог с upload_tmp_dir директивы upload_tmp_dir в php.ini . В любом случае, вы должны убедиться, что процесс PHP имеет надлежащие привилегии для записи в любой каталог, который используется.

  upload_tmp_dir = "/ tmp"

 tboronczyk @ zarkov: ~ $ ls -l / |  grep tmp
 drwxrwxrwt 13 root root 40960 2011-08-31 00:50 tmp

Убедившись, что конфигурация позволяет серверу принимать загруженные файлы, вы можете сосредоточиться на деталях HTML. Как и в случае большинства других взаимодействий на стороне сервера из HTML, при загрузке файлов используются формы. Обязательно, чтобы ваш элемент <form> использовал метод POST и enctype атрибут enctype установленный в multipart/form-data .

 <form action="upload.php" method="post" enctype="multipart/form-data"> 

Сценарии процесса загрузки

Вероятно, вы можете догадаться, что загрузка файлов рабочего процесса проходит на основе вашего собственного опыта и проверок требований, которые я только что упомянул.

  • Посетитель просматривает HTML-страницу с формой, специально написанной для поддержки загрузки файлов.
  • Посетитель предоставляет файл, который он хочет загрузить, и отправляет форму
  • Браузер кодирует файл и отправляет его как часть POST-запроса на сервер.
  • PHP получает отправку формы, декодирует файл и сохраняет его во временной папке на сервере.
  • PHP-скрипт, отвечающий за обработку сообщения формы, проверяет файл и обрабатывает его некоторым образом, часто перемещая его из временного местоположения в более постоянное место

Добавление поддержки для загрузки файлов требует, чтобы вы создали HTML-форму для представления пользователю и PHP-скрипт, чтобы заботиться о загруженном файле на сервере.

HTML

HTML-формы предоставляют интерфейс, через который пользователь инициирует загрузку файла. Помните, что для элемента <form> должен быть установлен атрибут method post а enctype атрибута enctypemultipart/form-data . Элемент <input> file предоставляет поле, используемое для указания файла, который будет загружен. Как и любой другой элемент формы, важно предоставить атрибут name, чтобы вы могли ссылаться на него в сценарии PHP, который обрабатывает форму.

Вот как выглядит разметка для основной формы загрузки файлов:

 <form action="upload.php" method="post" enctype="multipart/form-data"> <input type="file" name="myFile"> <br> <input type="submit" value="Upload"> </form> 

Стоит отметить, что разные браузеры отображают поле файла по-разному. IE, Firefox и Opera отображают его в виде текстового поля с кнопкой рядом с надписью «Обзор» или «Выбрать». Safari отображает его как кнопку с надписью «Выбрать файл». В большинстве случаев это не проблема. пользователи привыкли к тому, как поле отображается в выбранном браузере, и знают, как его использовать. Однако иногда вы сталкиваетесь с клиентом или дизайнером, который непреклонен в том, чтобы представить его определенным образом. Количество CSS и JavaScript, которые можно применить к файловому полю, крайне ограничено из-за соображений безопасности, навязанных браузерами. Стилизация поля файла может быть сложной. Если внешний вид важен для вас, я рекомендую вам ознакомиться со стилевым оформлением Питера-Пола Коха тип ввода = «file» .

PHP

Информация о загрузке файла доступна с помощью многомерного массива $_FILES . Этот массив индексируется именами, присвоенными полям файла в форме HTML, так же, как индексируются $_GET и $_POST . Массив каждого файла содержит следующие индексы:

  • $_FILES["myFile"]["name"] хранит исходное имя файла от клиента
  • $_FILES["myFile"]["type"] хранит mime-тип файла
  • $_FILES["myFile"]["size"] хранит размер файла (в байтах)
  • $_FILES["myFile"]["tmp_name"] хранит имя временного файла
  • $ _FILES [«myFile»] [«error»] хранит любой код ошибки, полученный в результате передачи

Функция move_uploaded_file() перемещает загруженный файл из временного в постоянное местоположение. Для этой цели всегда следует использовать move_uploaded_file() над такими функциями, как copy() и rename() поскольку он выполняет дополнительные проверки, чтобы убедиться, что файл действительно был загружен запросом HTTP POST.

Если вы планируете сохранить файл с исходным именем файла, предоставленным пользователем, рекомендуется убедиться, что это безопасно. Имя файла не должно содержать символов, которые могут повлиять на путь назначения, например косой черты. Имя не должно приводить к тому, что файл перезаписывает существующий файл с тем же именем (если только это не предназначено для вашего приложения). Я гарантирую безопасное имя файла, заменяя любые символы подчеркиванием, которые не являются буквой, цифрой или членом очень ограниченного набора знаков препинания, а затем добавляю увеличивающийся номер, когда файл с таким именем уже существует.

Вот как выглядит получение и обработка загрузки файла с помощью PHP:

 <?php define("UPLOAD_DIR", "/srv/www/uploads/"); if (!empty($_FILES["myFile"])) { $myFile = $_FILES["myFile"]; if ($myFile["error"] !== UPLOAD_ERR_OK) { echo "<p>An error occurred.</p>"; exit; } // ensure a safe filename $name = preg_replace("/[^A-Z0-9._-]/i", "_", $myFile["name"]); // don't overwrite an existing file $i = 0; $parts = pathinfo($name); while (file_exists(UPLOAD_DIR . $name)) { $i++; $name = $parts["filename"] . "-" . $i . "." . $parts["extension"]; } // preserve file from temporary directory $success = move_uploaded_file($myFile["tmp_name"], UPLOAD_DIR . $name); if (!$success) { echo "<p>Unable to save file.</p>"; exit; } // set proper permissions on the new file chmod(UPLOAD_DIR . $name, 0644); } 

Сначала код проверяет, загружен ли файл без ошибок. Затем он определяет безопасное имя файла, как я только что описал, и затем перемещает файл в его окончательный каталог, используя move_uploaded_file() . Наконец, есть вызов chmod() чтобы удостовериться, что для нового файла установлены права доступа.

Вопросы безопасности

Большинство из нас не позволяло бы совершенно незнакомым людям хранить случайные файлы на наших персональных компьютерах, и все же это именно то, что вы делаете, когда вы разрешаете загрузку файлов в нашем приложении. Вы можете захотеть, чтобы пользователь загрузил свою фотографию для страницы профиля, но что, если он попытается загрузить специально созданный, загруженный вирусом исполняемый файл? Я хотел бы поделиться несколькими шагами, которые вы можете предпринять, чтобы минимизировать риски безопасности, присущие разрешению загрузки файлов.

Один из них — проверить, какой тип загруженного файла должен быть. Использование значения $_FILES["myFile"]["type"] либо расширения файла не является безопасным, поскольку оба могут быть легко подделаны. Вместо этого используйте функцию наподобие exif_imagetype() чтобы проверить содержимое файла и определить, действительно ли это GIF, JPEG или один из нескольких других поддерживаемых форматов изображений. Если exif_imagetype() недоступна (функция требует, чтобы расширение Exif было включено), тогда вы можете использовать getimagesize() . Массив, возвращаемый getimagesize() будет содержать тип изображения, если он распознан.

 <?php // verify the file is a GIF, JPEG, or PNG $fileType = exif_imagetype($_FILES["myFile"]["tmp_name"]); $allowed = array(IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG); if (!in_array($fileType, $allowed)) { // file type is not permitted ... 

Для файлов, не относящихся к изображениям, вы можете использовать exec() для вызова утилиты Unix file . file определяет тип файла путем поиска известных двоичных сигнатур в ожидаемых местах.

 <?php // verify the file is a PDF $mime = "application/pdf; charset=binary"; exec("file -bi " . $_FILES["myFile"]["tmp_name"], $out); if ($out[0] != $mime) { // file is not a PDF ... 

Еще один шаг, который вы можете предпринять, это наложить жесткие ограничения на общий размер запроса POST и количество файлов, которые можно загрузить. Для этого укажите соответствующее значение для директив upload_max_size , post_max_size и max_file_uploads в php.ini . Директива upload_max_size указывает максимальный размер загрузки файла. В дополнение к размеру загрузки вы можете ограничить размер всего запроса POST с помощью директивы post_max_size . max_file_uploads — это более новая директива (добавлена ​​в версии 5.2.12), которая ограничивает количество загрузок файлов. Эти три директивы помогают защитить ваш сайт от атак, которые пытаются нарушить его доступность, вызывая большой сетевой трафик или нагрузку на систему.

  post_max_size = 8M
 upload_max_size = 2M
 max_file_uploads = 20

Третий шаг, который вы можете предпринять, чтобы минимизировать риск, — это сканирование загруженных файлов с помощью антивирусного сканера. Это жизненно важно в наши дни и в эпоху широко распространенных вирусов и вредоносных программ, особенно если ваш сайт впоследствии делает загруженные файлы доступными для загрузки другими пользователями, например, с помощью вложений в веб-почтовом клиенте или (легальном) файлообменном сайте. , Существует расширение PHP , обеспечивающее доступ к ClamAV , но, конечно, вы можете вызывать утилиту командной строки ClamAV во многом так же, как я продемонстрировал для file .

 <?php exec("clamscan --stdout " . $_FILES["myFile"]["tmp_name"], $out, $return); if ($return) { // file is infected ... 

Резюме

Вы узнали, как легко поддерживать загрузку файлов с вашего сайта или веб-приложения. Чтобы загрузка прошла успешно, HTML-форма должна быть отправлена ​​через POST-запрос, закодированный в multipart/form-data , и PHP должен разрешить передачу, как указано с file_uploads директивы file_uploads . После передачи файла скрипт, отвечающий за обработку загрузки, использует информацию, найденную в массиве $_FILES для перемещения файла из временного каталога в нужное место. Я также поделился некоторыми дополнительными мерами предосторожности, которые вы можете предпринять, чтобы защитить себя и своих пользователей от некоторых рисков, связанных с разрешением загрузки файлов. Вы увидели, как можно обеспечить безопасность имени файла, проверить тип файла, наложить жесткие ограничения на загрузку трафика и выполнить сканирование на наличие вирусов.

Для тех, кто может быть заинтересован, дополнительный код для этой статьи доступен на GitHub. Вы можете просматривать, загружать или клонировать репозиторий и играть с кодом, чтобы лучше понять, как работает процесс загрузки файлов.

И если вам понравилось читать этот пост, вы полюбите Learnable ; место, чтобы узнать новые навыки и приемы у мастеров. Участники получают мгновенный доступ ко всем электронным книгам SitePoint и интерактивным онлайн-курсам, таким как Jump Start PHP .

Комментарии к этой статье закрыты. Есть вопрос по PHP? Почему бы не спросить об этом на наших форумах ?

Изображение через VolsKinvols / Shutterstock