Закачать файл на сервер php. Отправка файлов на сервер. Требования перед загрузкой файлов
Но в начале я бы хотел сказать пару слов о том, как же происходит загрузка файлов.
Для этого нам понадобится html форма с полем ввода типа . Кроме того, для передачи файлов на сервер необходимо форме установить тип multipart . Для этого в качестве параметра enctype указывается значение multipart/form-data .
После того, как на html страничке мы разместим форму с полем в окне браузера отобразится поле с возможностью выбрать файл на локальном компьютере.
После того, как пользователь выберет нужный файл и нажмет кнопку «Загрузить» форма передаст данные php скрипту на сервер, который указан в action формы. Если action формы пустой, то данные будут переданы тому же файлу, на котором находится форма. Вся информация о загружаемом файле помещается в массив $ _FILES . Нам лишь остается извлечь эту информацию и переместить файл в необходимое нам место.
Прежде, чем приступить к написанию скрипта обработки multipart-формы , нужно отредактировать файл конфигурации php.ini , чтобы разрешить загрузку файлов на сервер.
Конфигурационный файл PHP php.ini имеет три параметра, связанные с загрузкой файлов на сервер:
- file_uploads = On - разрешает загрузку файлов на сервер по протоколу HTTP;
- upoad_tmp_dir = /tmp - устанавливает каталог для временного хранения загруженных файлов;
- upload_max_filesize = 2M - устанавливает максимальный объем загружаемых файлов.
Итак, создайте новый файл с именем upload.php и скопируйте в него следующий код.
Если внимательно посмотреть на форму, то Вы увидите скрытое поле
Оно указывает на максимальный размер принимаемого файла в байтах. Но не следует доверять этому значения, так как оно является лишь уведомляющим и его легко можно обойти! Так что имейте это в виду!
После того как пользователь выбрал файл и нажал на кнопку «Загрузить», вся информация о файле, как упоминалось ранее, помещается в массив $ _FILES, а сам файл помещается во временный каталог на сервере, который указан в в php.ini.
Так как поле file называлось name="uploadFile" , то массив $ _FILES будет содержать ассоциативный массив с ключом "uploadFile" .
- $_FILES[" uploadFile "]["name"] - имя файла до его отправки на сервер, например, pict.gif;
- $_FILES[" uploadFile "]["size"] - размер принятого файла в байтах;
- $_FILES[" uploadFile "]["type"] - MIME-тип принятого файла (если браузер смог его определить), например: image/gif, image/png, image/jpeg, text/html;
- $_FILES[" uploadFile "]["tmp_name"] - содержит имя файла во временном каталоге, например: /tmp/phpV3b3qY;
- $_FILES[" uploadFile "]["error"] - Код ошибки, которая может возникнуть при загрузке файла.
После завершения работы скрипта, временный файл будет удален. Это означает, что мы должны его скопировать в другое место до завершения работы скрипта.
Итак, с алгоритмом разобрались! Теперь давайте взглянем на код.
Первым делом мы проверяем была ли нажата кнопка submit.
If(isset($_POST["upload"])) { }
If(is_uploaded_file($_FILES["uploadFile"]["tmp_name"])) { Выполняем действие над файлом } else { echo "Файл не загружен"; }
Если файл был загружен через HTTP POST, то далее мы его перемещаем из временного каталога в нужный нам каталог. Это делается при помощи функции move_uploaded_file, которая принимает два параметра: имя файла для загрузки и путь куда будет перемещен файл. В случае успешного перемещения файла, данная функция возвратит true, в противном случае -false.
If(move_uploaded_file($_FILES["uploadFile"]["tmp_name"], $uploadedFile)) { echo Файл загружен; } else { echo Во время загрузки файла произошла ошибка; }
В нашем случае в качестве имени файла выступает имя временного файла на сервере - $_FILES["uploadFile"]["tmp_name"], а в качестве каталога, куда будет перемещен файл - переменная $uploadedFile, которая было объявлена выше в скрипте и содержит новое место хранения файла.
$folder = "path/to/folder/"; $uploadedFile = $folder. basename($_FILES["uploadFile"]["name"]);
Из своего опыта могу сказать, что хранить оригинальное название файла на сервере не стоит, поэтому его можно переименовать. Для этого сгенерируем случайное название для нашего файла и функция move_uploaded_file() переместит и переименует наш файл:
// Получаем расширение файла $file_ext = strtolower(strrchr($_FILES["uploadFile"]["name"],".")); // Генерируем случайное число $file_name = uniqid(rand(10000,99999)); // Формируем путь на сервере $uploadedFile = $folder.$file_name.$file_ext;
Ну и напоследок приведу список возможных ошибок, которые возникают во время загрузки файлов на сервер. Напоминаю, что код ошибки хранится в переменной $_FILES[" uploadFile "]["error"]:
- 0- Ошибок не возникало, файл был успешно загружен на сервер.
- 1- Размер принятого файла превысил максимально допустимый размер, который задан директивой upload_max_filesize конфигурационного файла php.ini..
- 2- Размер загружаемого файла превысил значение MAX_FILE_SIZE , указанное в HTML-форме.
- 3- Загружаемый файл был получен только частично.
- 4- Файл не был загружен.
- 6- Нет временную папку.
- 7- Сбой при записи файлов на диск.
Как видите, организовать загрузку файлов на сервер не так уж и сложно. Сложнее обеспечить необходимый уровень безопасности, так как загрузка файлов на сервер может использоваться злоумышленниками для атаки на сервер.
При помощи нашего скритпа злоумышленник сможет загружать произвольные файлы на сервер, к примеру, он сможет закачать на сервер php-скрипт, который рекурсивно сможет удалить все ваши файлы на сервере или PHP-shell, так что если вы пишите свой загрузчик файлов, то к этому делу надо подойти серьезно, ничего не упустив.
В нашем примере я не ставил перед собой такой задачи, а лишь показал Вам весь механизм загрузки файлов на сервер, но в следующей статье, я покажу, как обеспечить необходимый уровень безопасности!
Последнее обновление: 1.11.2015
Чтобы загрузить файл на сервер, нам надо использовать форму с параметром enctype="multipart/form-data" и массив $_FILES . Итак, создадим файл upload.php со следующим содержимым:
Загрузка файла
Здесь определена форм с атрибутом enctype="multipart/form-data" . Форма содержит специальное поле для выбора файла.
Все загружаемые файлы попадают в ассоциативный массив $_FILES . Чтобы определить, а есть ли вообще загруженные файлы, можно использовать конструкцию if: if ($_FILES)
Массив $_FILES является двухмерным. Мы можем загрузить набор файлов, и каждый загруженный файл можно получить по ключу, который совпадает со значением атрибута name .
Так как элемент для загрузки файла на форме имеет name="filename" , то данный файл мы можем получить с помощью выражения $_FILES["filename"] .
У каждого объекта файла есть свои параметры, которые мы можем получить:
$_FILES["file"]["name"] : имя файла
$_FILES["file"]["type"] : тип содержимого файла, например, image/jpeg
$_FILES["file"]["size"] : размер файла в байтах
$_FILES["file"]["tmp_name"] : имя временного файла, сохраненного на сервере
$_FILES["file"]["error"] : код ошибки при загрузке
Также мы можем проверить наличие ошибок при загрузке. Если у нас нет ошибки, то поле $_FILES["filename"]["error"] содержит значение UPLOAD_ERR_OK .
При отправке файла на сервер он сначала загружается во временное место, из которого затем с помощью функции move_uploaded_file() он перемещается в каталог сервера.
Функция move_uploaded_file() принимает два параметра путь к загруженному временному файлу и путь, куда надо поместить загруженный файл.
Ограничения и настройка загрузки
По умолчанию размер загружаемых файлов ограничен 2 мб. Однако можно настроить данный показатель в файле конфигурации. Изменим этот показатель, например, до 10 мб. Для этого найдем в файле php.ini следующую строку:
Upload_max_filesize = 2M
Изменим ее на
Upload_max_filesize = 10M
Также мы можем настроить папку для временных загружаемых файлов. Для этого в файле php.ini найдем следующую строку:
;upload_tmp_dir =
Изменим ее на
Upload_tmp_dir = "C:/php/upload"
Также в каталоге php нам надо создать папку upload .
Мультизагрузка
Изменим скрипт upload.php так, чтобы он поддерживал множественную загрузку:
$error) { if ($error == UPLOAD_ERR_OK) { $tmp_name = $_FILES["uploads"]["tmp_name"][$key]; $name = $_FILES["uploads"]["name"][$key]; move_uploaded_file($tmp_name, "$name"); } } } ?>
Загрузка файла
Каждое поле выбора файла имеет атрибут name="uploads" , поэтому сервер будет рассматривать набор отправленных файлов как единый массив.
Затем используя цикл foreach , проходим по все файлам и сохраняем их в каталог веб-сайта.
Multipart-формы
- Веб-итерфейсы почтовых сервисов, которые позволяют добавалять к письму приложение (attach), а для этого нужно сначала загрузить файл на сервер, и только после этого его можно добавлять к письму;
- Интерактивные фотогалереи и фотоальбомы, которые не могут существовать без механизма загрузки файлов на сервер;
- Порталы бесплатного програмного обеспечения, которые используют для обмена файлами различных программ, и.т.д.
Загрузка файла на сервер осуществляется с помощью multipart -формы, в которой есть поле загрузки файла. В качестве параметра enctype указывается значение multipart/form-data :
Вот так примерно будет выглядеть приведенная multipart-форма (вы можете попробовать с ее помощью посмотреть результат работы multipart-форм, загрузив какой-нибудь файл небольшого размера на сервер):
Multipart-формы обычно используют метод передачи POST. Как видно из предыдущего примера, данная форма имеет два поля:
- Поле выбора файла для закачки ;
- Поле указания имени файла, которое он должен будет иметь на сервере .
Обработка multipart-форм
Прежде, чем приступить к написанию скрипта обработки multipart-формы, нужно отредактировать файл конфигурации php.ini , чтобы разрешить загрузку файлов на сервер.
Конфигурационный файл PHP php.ini имеет три параметра, связанные с загрузкой файлов на сервер:
- file_uploads=On - разрешает загрузку файлов на сервер по протоколу HTTP;
- upload_tmp_dir=/tmp - устанавливает каталог для временного хранения загруженных файлов;
- upload_max_filesize=2M - устанавливает максимальный объем загружаемых файлов.
Если ваш веб-сервер работает под управлением операционной системы Linux, то нужно перезапустить сервис:
service httpd restart
Как же PHP обрабатывает multipart-формы? Получив файл, он сохраняет его во временном каталоге upload_tmp_dir , имя файла выбирается случайным образом. Затем он создает четыре переменных суперглобального массива $_FILES . Этот массив содержит информацию о загруженном файле.
Переменные, определенные для загруженных файлов, зависят от версии PHP и текущей конфигурации. Суперглобальный массив $_FILES доступен начиная с PHP 4.1.0. В случае, если конфигурационная директива register_globals установлена значением on , дополнительно будут объявлены переменные с соответствующими именами. Начиная с версии 4.2.0 значением по умолчанию для опции register_globals является off .
Содержимое массива $_FILES для нашего примера приведено ниже. Обратите внимание, что здесь предполагается использование имени uploadfile для поля выбора файла, в соответствии с приведенной выше multipart-форме. Разумеется, имя поля может быть любым.
- $_FILES["uploadfile"]["name"] - имя файла до его отправки на сервер, например, pict.gif;
- $_FILES["uploadfile"]["size"] - размер принятого файла в байтах;
- $_FILES["uploadfile"]["type"] - MIME-тип принятого файла (если браузер смог его определить), например: image/gif, image/png, image/jpeg, text/html;
- (так мы назвали поле загрузки файла) - содержит имя файла во временном каталоге, например: /tmp/phpV3b3qY;
- $_FILES["uploadfile"]["error"] - Код ошибки, которая может возникнуть при загрузке файла. Ключ ["error"] был добавлен в PHP 4.2.0. С соответствующими кодами ошибок вы можете ознакомиться
После завершения работы скрипта, временный файл будет удален. Это означает, что мы должны его скопировать в другое место до завершения работы скрипта. То есть алгоритм работы сценария загрузки файла на сервер такой:
Если кнопка "Submit" нажата, то файл уже будет загружен на сервер и его имя будут в переменной $_FILES["uploadfile"]["name"] . В этом случае скрипт должен сразу скопировать файл с именем $_FILES["uploadfile"]["tmp_name"] в какой-нибудь каталог (необходимы права на запись в этот каталог).
Копирование файла производится функцией copy() :
Используйте только функцию копирования copy() , а не перемещения, поскольку:
- Временный файл будет удален автоматически;
- Если временный каталог находится на другом носителе, будет выведено сообщение об ошибке.
Предположим, нам нужно загрузить файл в каталог uploads , который находится в корневом каталоге веб-сервера (в каталоге DocumentRoot ).
// На всякий случай создадим каталог. Если он уже создан,
// сообщение об ошибки мы не увидим, поскольку воспользуемся оператором @:
@mkdir("uploads", 0777);
// Копируем файл из /tmp в uploads
// Имя файла будет таким же, как и до отправки на сервер:
Copy($_FILES["uploadfile"]["tmp_name"],"uploads/".basename($_FILES["uploadfile"]["name"]));
В Linux все намного сложнее - нам нужно учитывать права доступа к каталогу uploads . Скорее всего в таком случае, функция mkdir() не сработает, так как у нас нет прав на запись в каталог DocumentRoot (обычно это /var/www/html или /home/httpd/html). Зарегистрируйтесь в системе как пользователь root , создайте каталог uploads и измените его владельца и права доступа следующим образом:
// Создаем каталог uploads
// Устанавливаем имя владельца apache и его группу - тоже apache:
Chown apache:apache uploads
// Разрешение записи всем (777) + установка закрепляющего бита (1):
Chmod 1777 uploads
Размер файла можно ограничить, при желании можно отредактировать файл .htaccess и ограничить доступ к каталогу uploads - указать или конкретных пользователей, которым можно обращаться к каталогу, или IP-адреса.
Вот теперь можно загружать файлы на сервер.
Пишем PHP скрипт загрузки файлов на сервер
php Оригинальное имя загруженного файла: "
// Каталог, в который мы будем принимать файл:
$
uploaddir
=
"./files/"
;
$
uploadfile
=
$
uploaddir
.
basename
($
_FILES
[
"uploadfile"
][
"name"
]);
// Копируем файл из каталога для временного хранения файлов:
if
(copy
($
_FILES
[
"uploadfile"
][
"tmp_name"
],
$
uploadfile
))
{
echo
"Файл успешно загружен на сервер
"
;
}
else
{
echo
"Ошибка! Не удалось загрузить файл на сервер!
"
;
exit
;
}
// Выводим информацию о загруженном файле:
echo
"Информация о загруженном на сервер файле:
"
;
echo
"
echo "
Mime-тип загруженного файла: "
.$ _FILES [ "uploadfile" ][ "type" ]. "" ;echo "
Размер загруженного файла в байтах: "
.$ _FILES [ "uploadfile" ][ "size" ]. "" ;echo "
Временное имя файла: "
.$ _FILES [ "uploadfile" ][ "tmp_name" ]. "" ;?>
Загрузку нескольких файлов можно реализовать используя, например, различные значения name для тега input .
Также предусмотрена возможность автоматического получения организованной в массив информации о нескольких одновременно загружаемых файлах. Для реализации такой возможности используйте тот же синтаксис отправки массива из HTML-формы, что и для множественных полей select и checkbox :
В случае, если такая форма была отправлена, массивы $_FILES["userfile"] , $_FILES["userfile"]["name"] , и $_FILES["userfile"]["size"] будут инициализированы (точно так же, как и $HTTP_POST_FILES для PHP 4.1.0 и более ранних версий). Если конфигурационная директива register_globals установлена значением on , также будут инициализированы сопутствующие глобальные переменные. Каждая из таких переменных будет представлять собой численно индексированный массив соответствующих значений для принятых файлов.
Предположим, что были загружены файлы /home/test/some.html и /home/test/file.bin . В таком случае переменная $_FILES["userfile"]["name"] будет иметь значение some.html , а переменная $_FILES["userfile"]["name"] - значение file.bin . Аналогично, переменная $_FILES["userfile"]["size"] будет содержать размер файла some.html и так далее.
Переменные $_FILES["userfile"]["name"] , $_FILES["userfile"]["tmp_name"] , $_FILES["userfile"]["size"] и $_FILES["userfile"]["type"] также будут инициализированы.
Заключение:
Как видите, организовать загрузку файлов на сервер не так сложно. Сложнее обеспечить необходимый уровень безопасности, так как загрузка файлов на сервер может использоваться злоумышленниками для атаки на сервер. О том, как обеспечить необходимый уровень безопасности, работая с Uploads , смотрите .
<<< Назад | Содержание | Вперед >>> |
Есть еще вопросы или что-то непонятно - добро пожаловать на наш | |
|
Практически в каждом проекте разработчик сталкивается с необходимостью загрузки файлов на сервер. Фотографии, картинки, документы, архивы и многое другое может быть загружено на сервер посредством web-интерфейса (через браузер).
Http://localhost/index.php?name=myname&page=10
определяет запрос GET к странице index.php. При этом скрипту передаются параметры "name" и "page" со значениями "myname" и "10" соответственно. Как видите, запрос состоит из пар "имя=значение", объединённых через "&". Запрос отделяется от адреса страницы знаком вопроса "?".
Но GET не подходит для передачи больших данных. Для этого существует метод POST. Этот метод передаёт данные скрыто от пользователя, а адрес страницы остаётся неизменным. Теоретически, метод POST позволяет передавать огромные блоки данных, но большинство провайдеров устанавливают жесткое ограничение в 5-10 мегабайт.
Для реализации загрузки файла необходимо снабдить пользователя соответствующей формой. Для загрузки файлов предназначен тег input типа file.
Пользователь выбирает нужный файл, нажимает кнопку "Отправить!", после чего браузер передаёт указанный файл на сервер. По окончании загрузки файла запускается receiver.php, и ему автоматически становятся доступны все переданные в форме данные.
Список загруженных файлов хранится в глобальном массиве $_FILES. Это ассоциативный массив, где каждый элемент содержит описание одного из файлов. Рассмотрим на примере:
3145728) { $filesize = ($filesize != 0)? sprintf("(%.2f Мб)" , $filesize / 1024): ""; die("Ошибка: Размер прикреплённого файла ". $filesize." больше допустимого (3 Мб)."); } else { $filename = $_FILES["file"]["name"]; $filepath = $_FILES["file"]["tmp_name"]; $filetype = $_FILES["file"]["type"]; if($this->filetype == null || $this->filetype == "") $this->filetype = "unknown/unknown"; } } } echo "Успешно загружен файл: " . $filename; ?>
Все загруженные файлы сохраняются сервером во временном каталоге и автоматически удаляются после завершения скрипта. Если вам необходимо сохранить файл на сервере - его надо переместить в один из каталогов сайта. Для этого служит специальная функция move_uploaded_file() . Обчно она используется в паре с функцией is_uploaded_file() , которая позволяет определить, был ли файл загружен через форму. Например:
Информация о каждом загруженном файле представляется в скрипте как элемены массива $_FILES. Имя элемента определяется атрибутом name тега формы. В нашем случае атрибут name равен "ufile", значит данные об этом файле будут храниться в элементе $_FILES["ufile"].
Каждый элемент $_FILES так же является массивом, содержащим пять элементов с фиксированными именами:
Размер загружаемых файлов может значительно меняться от провайдера к провайдеру. Некоторые администраторы вообще запрещают загрузку файлов. В этом случае файл всё равно может загрузиться, сервер его удалит, а скрипту сообщит, что файл был, но уже удалён.
Если Вам потребовалось отдавать файлы не напрямую веб сервером, а с помощью PHP (например для сбора статистики скачиваний), прошу под кат.
1. Используем readfile()
Метод хорош тем, что работает с коробки. Надо только написать свою функцию отправки файла (немного измененный пример из официальной документации):Function file_force_download($file) {
if (file_exists($file)) {
// сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт
// если этого не сделать файл будет читаться в память полностью!
if (ob_get_level()) {
ob_end_clean();
}
// заставляем браузер показать окно сохранения файла
header("Content-Description: File Transfer");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=" . basename($file));
header("Content-Transfer-Encoding: binary");
header("Expires: 0");
header("Cache-Control: must-revalidate");
header("Pragma: public");
header("Content-Length: " . filesize($file));
// читаем файл и отправляем его пользователю
readfile($file);
exit;
}
}
Таким способом можно отправлять даже большие файлы, так как PHP будет читать файл и сразу отдавать его пользователю по частям. В документации четко сказано, что readfile() не должен
создавать проблемы с памятью.
Особенности:
- Файл читается в внутренний буфер функции readfile(), размер которого составляет 8кБ (спасибо 2fast4rabbit)
2. Читаем и отправляем файл вручную
Метод использует тот же Drupal при отправке файлов из приватной файловой системы (файлы недоступны напрямую по ссылкам):Function file_force_download($file) {
if (file_exists($file)) {
// сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт
// если этого не сделать файл будет читаться в память полностью!
if (ob_get_level()) {
ob_end_clean();
}
// заставляем браузер показать окно сохранения файла
header("Content-Description: File Transfer");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=" . basename($file));
header("Content-Transfer-Encoding: binary");
header("Expires: 0");
header("Cache-Control: must-revalidate");
header("Pragma: public");
header("Content-Length: " . filesize($file));
// читаем файл и отправляем его пользователю
if ($fd = fopen($file, "rb")) {
while (!feof($fd)) {
print fread($fd, 1024);
}
fclose($fd);
}
exit;
}
}
Особенности:
- Скрипт ждет пока весь файл будет прочитан и отдан пользователю.
- Позволяет сэкономить память сервера
3. Используем модуль веб сервера
3a. Apache
Модуль XSendFile позволяет с помощью специального заголовка передать отправку файла самому Apache. Существуют версии по Unix и Windows, под версии 2.0.*, 2.2.* и 2.4.*В настройках хоста нужно включить перехват заголовка с помощью директивы:
XSendFile On
Также можно указать белый список директорий, файлы в которых могут быть обработаны. Важно: если у Вас сервер на базе Windows путь должен включать букву диска в верхнем регистре.
Описание возможных опций на сайте разработчика: https://tn123.org/mod_xsendfile/
Пример отправки файла:
Function file_force_download($file) { if (file_exists($file)) { header("X-SendFile: " . realpath($file)); header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename=" . basename($file)); exit; } }
3b. Nginx
Nginx умеет отправлять файлы из коробки через специальный заголовок.Для корректной работы нужно запретить доступ к папку напрямую через конфигурационный файл:
location /protected/ {
internal;
root /some/path;
}
Пример отправки файла (файл должен находиться в директории /some/path/protected):
Function file_force_download($file) {
if (file_exists($file)) {
header("X-Accel-Redirect: " . $file);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=" . basename($file));
exit;
}
}
Больше информации на странице официальной документации
Особенности:
- Скрипт завершается сразу после выполнения всех инструкций
- Физически файл отправляется модулем самого веб сервера, а не PHP
- Минимальное потребление памяти и ресурсов сервера
- Максимальное быстродействие
Update: Хабраюзер ilyaplot дает дельный совет, что лучше слать не application/octet-stream , а реальный mime type файла. Например, это позволит браузеру подставить нужные программы в диалог сохранение файла.