Оглавление
Настройка параметров PHP
Для начала, нам потребуется настроить некоторые параметры php, для этого необходимо отредактировать конфигурационный файл php.ini. Если у вас нет доступа к этому файлу, обратитесь в техническую поддержку вашего хостинга. Я же использую локальный сервер OpenServer и для настройки php нужно перейти в раздел Дополнительно -> Конфигурация -> PHP (ваша версия PHP). Укажем следующие настройки:;Время выполнения скрипта в секундах (максимальное), обычно стараются укладывать в 30-60 секунд, для примера задам 3 минуты
max_execution_time = 180
;Максимальное потребление памяти одним php скриптом
memory_limit = 128M
;Флаг загрузки файлов (On - можно Off - нельзя)
file_uploads = On
;Максимально разрешённое количество одновременно загружаемых файлов
max_file_uploads = 20
;Максимально допустимый размер данных, отправляемых методом POST
post_max_size = 100M
;Папка для временного хранения файлов во время загрузки, я использую OpenServer поэтому в пути присутсвует спец.переменная %sprogdir%
upload_tmp_dir = "%sprogdir%/userdata/temp/"
;Максимально допустимый размер загружаемого файла
upload_max_filesize = 5M
Перезагружаем сервер, чтобы изменения вступили в силу. Можно приступать к коду.
Загрузка одного файла из веб-формы
Описываем на примере загрузки аватара в профиль пользователя на форуме. Чтобы форма имела какой-нибудь «приличный» вид, я подключил css bootstrap используя cdn.Сама форма максимально простая, выглядит так:
<div class="container">
<div class="row">
<div class="col-6">
<h1>Загрузка файла на сервер</h1>
<form action="profile.php" method="post" enctype="multipart/form-data">
<label for="user_avatar" class="form-label">Аватар пользователя</label>
<input class="form-control" type="file" id="user_avatar" name="user_avatar"><br/>
<button type="submit" class="btn btn-primary">Отправить</button>
</form>
</div>
<div class="col-6"></div>
</div>
</div>
Файл profile.php который будет отвечать за обработку загружаемого изображения пока содержит следующий код:
echo '<pre>'; print_r($_FILES); echo '</pre>';
Все передаваемые на сервер файлы попадают в так называемый «суперглобальный массив» $_FILES, приведённый выше код покажет нам его содержимое. Отправим тестовый файл, у меня это картинка формата jpg размером 100x100 пикселей. Получим следующее содержимое $_FILES:
Array(
[user_avatar] => Array
(
[name] => avatar.jpg
[type] => image/jpeg
[tmp_name] => F:\OpenServer\userdata\php_upload\php6F8A.tmp
[error] => 0
[size] => 6933
)
)
Обратите внимание что все данные о загружаемом файле хранятся в виде ассоциативного массива, при этом ключ массива user_avatar равен значению атрибута name в форме загрузки файла. Сам файл при этом попадает во временную директорию на сервере tmp_name которую мы указывали в php.ini нашего сервере.
В случае каких-то ошибок в поле error этого ассоциативного массива будут попадать коды ошибок загрузки файла. К этим кодам относятся:
- UPLOAD_ERR_OK - Ошибок не возникло, файл был успешно загружен на сервер;
- UPLOAD_ERR_INI_SIZE - Размер принятого файла превысил максимально допустимый размер;
- 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-расширение остановило загрузку файла.
Давайте выполним ряд проверок загружаемого файла:
// Массив c описанием ошибок
$uploadErrorMessages = [
'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-расширение остановило загрузку файла.',
];
// Проверяем что $_FILES есть наш файл
if (isset($_FILES['user_avatar'])) {
$image = $_FILES['user_avatar'];
// Получаем нужные элементы массива "user_avatar"
$tmpFileName = $_FILES['user_avatar']['tmp_name'];
$uploadErrorCode = $_FILES['user_avatar']['error'];
//Если есть ошибка или файл был загружен не через HTTP POST вернём ошибку
if ($uploadErrorCode !== UPLOAD_ERR_OK || !is_uploaded_file($tmpFileName)) {
//Выводим соответствующее $errorCode сообщение об ошибка или "ошибка неизвестна"
$message = isset($uploadErrorMessages[$uploadErrorCode]) ? $uploadErrorMessages[$uploadErrorCode] : 'При загрузке файла произошла неизвестная ошибка.';
//Завершаем работу скрипта, выводим сообщение об ошибке
echo json_encode(['status'=>'error', 'msg'=>$message]);
} else {
//Если всё ок, выполняем манипуляции с файлом
echo json_encode(['status'=>'ok', 'msg'=>'Ошибок нет']);
}
}
Здесь я проверяю что если код ошибки не равен UPLOAD_ERR_OK или функция is_uploaded_file() вернула false (т.е. файл был загружен не через HTTP POST а как-то иначе), то подготавливаем переменную $message и возвращаем JSON строку с ошибкой. Если всё ок, то выводится сообщение что ошибок нет, опять таки в виде JSON. Это сделано для того, чтобы более удобно взаимодействовать с фронт-частью нашего приложения.
Давайте сохраним файл в папку /img/ на нашем учебном сервере. Добавим следующий код в условие, где не возникло ошибок при загрузке:
//Если всё ок, выполняем манипуляции с файлом
if (move_uploaded_file($_FILES['user_avatar']['tmp_name'], $uploadDir . $_FILES['user_avatar']['name'])) {
echo json_encode(['status'=>'ok', 'msg'=>'Файл загружен в директорию ' . $uploadDir]);
} else {
echo json_encode(['status'=>'error', 'msg'=>'Ошибка загрузки файла']);
}
А так же выведем список файлов в папке /img/:
<div class="container">
<div class="row">
<div class="col-6">
<h1>Загрузка файла на сервер</h1>
<form action="profile.php" method="post" enctype="multipart/form-data">
<label for="user_avatar" class="form-label">Аватар пользователя</label>
<input class="form-control" type="file" id="user_avatar" name="user_avatar"><br/>
<button type="submit" class="btn btn-primary">Отправить</button>
</form>
</div>
<div class="col-6">
<h2>Список файлов в /img/</h2>
<ul>
<?
$uploadDir = $_SERVER['DOCUMENT_ROOT'] . '/fileupload/img/';
$arFiles = scandir($uploadDir, 1);
if(is_array($arFiles)) {
foreach ($arFiles as $file)
{
?>
<li><a href="img/<?=$file?>" target="_blank"><?=$file?></a></li>
<?
}
}
?>
</ul>
</div>
</div>
</div>
Получаем такую картинуВ случае успешной загрузки, в списке файлов справа появиться ссылка на загруженный файл. Давайте добавим дополнительную проверку на тип файла, например, нас интересуют только изображения.
Множественная загрузка файлов на сервер
Добавим ещё одну форму в наш код.
<div class="container">
<div class="row">
<div class="col-6">
<h1>Загрузка файла на сервер</h1>
<form action="profile.php" method="post" enctype="multipart/form-data">
<label for="user_avatar" class="form-label">Аватар пользователя</label>
<input class="form-control" type="file" id="user_avatar" name="user_avatar"><br/>
<button type="submit" class="btn btn-primary">Отправить</button>
</form>
<br>
<hr>
<br>
<h2>Множественная загрузка файлов</h2>
<form action="gallery.php" method="post" enctype="multipart/form-data">
<label for="gallery" class="form-label">Фотогалерея</label>
<input class="form-control" type="file" id="gallery" name="gallery[]" multiple><br/>
<button type="submit" class="btn btn-primary">Отправить</button>
</form>
</div>
<div class="col-6">
<h2>Список файлов в /img/</h2>
<ul>
<?
$uploadDir = $_SERVER['DOCUMENT_ROOT'] . '/fileupload/img/';
$arFiles = scandir($uploadDir, 1);
if(is_array($arFiles)) {
foreach ($arFiles as $file)
{
?>
<li><a href="img/<?=$file?>" target="_blank"><?=$file?></a></li>
<?
}
}
?>
</ul>
</div>
</div>
</div>
Обратите внимание на то, что в name поля загрузки файла появились квадратные скобки gallery[] а так же атрибут multiple. С такими параметрами вы сможете выбрать несколько файлов зажав кнопку Ctrl в окне просмотра файлов. Форма приобрела следующий вид.
Я так же добавил отдельный скрипт для обработки множественной загрузки gallery.php. В целом он имеет ту же структуру, однако файлы теперь обрабатываются в цикле из заранее подготовленного массива $arFiles.
header('Content-Type: application/json');
// Массив c описанием ошибок
$uploadErrorMessages = [
'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-расширение остановило загрузку файла.',
];
// Проверяем что $_FILES есть наш файл
if (isset($_FILES['gallery'])) {
$uploadDir = $_SERVER['DOCUMENT_ROOT'] . '/fileupload/img/';
$arFiles = [];
//Изменим структуру массива $_FILES для удобства работы с файлами
foreach($_FILES['gallery'] as $field => $arValues) {
foreach($arValues as $key => $value) {
$arFiles[$key][$field] = $value;
}
}
foreach ($arFiles as $key => $file){
// Получаем нужные элементы массива "user_avatar"
$tmpFileName = $file['tmp_name'];
$uploadErrorCode = $file['error'];
//Если есть ошибка или файл был загружен не через HTTP POST вернём ошибку
if ($uploadErrorCode !== UPLOAD_ERR_OK || !is_uploaded_file($tmpFileName)) {
//Выводим соответствующее $errorCode сообщение об ошибка или "ошибка неизвестна"
$message = isset($uploadErrorMessages[$uploadErrorCode]) ? $uploadErrorMessages[$uploadErrorCode] : 'При загрузке файла произошла неизвестная ошибка.';
//Завершаем работу скрипта, выводим сообщение об ошибке
echo json_encode(['status'=>'error', 'msg'=>$message], JSON_UNESCAPED_UNICODE);
} else {
//Добавим проверку на тип файла
$allowedFormats = ['image/jpeg', 'image/gif', 'image/png'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$fileInfo = finfo_file($finfo, $tmpFileName);
if(in_array($fileInfo, $allowedFormats)){
//Если всё ок, выполняем манипуляции с файлом
if (move_uploaded_file($file['tmp_name'], $uploadDir . $file['name'])) {
echo json_encode(['status'=>'ok', 'msg'=>'Файл загружен в директорию ' . $uploadDir], JSON_UNESCAPED_UNICODE);
} else {
echo json_encode(['status'=>'error', 'msg'=>'Ошибка загрузки файла'], JSON_UNESCAPED_UNICODE);
}
} else {
echo json_encode(['status'=>'error', 'msg'=>'Можно загружать только изображения jpg, png или gif формата'], JSON_UNESCAPED_UNICODE);
}
}
}
}
А текущем варианте скрипта отсутствует ajax и всякий js способный улучшить пользовательский опыт. Полную версию скрипта я опишу в разделе bitrix этого сайта где буду делать простой компонент множественной ajax загрузки файлов на сервер.