Загрузка файлов на сайт: PHP, AJAX, HTML5 и Drag’n’Drop

Скучные формы загрузки — прошлый век. HTML5 дает возможности, чтобы добавить Drag’n’Drop, а AJAX позволяет загружать файлы без обновления страницы.

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

Программисту нужно позаботиться о том, чтобы посетитель смог сделать это максимально удобно. Загрузить файл на сайт можно и с помощью обычной формы и обработчика на PHP, но с выходом HTML5 появились другие интересные возможности — в этой статье мы поговорим и о базовых функциях, и о нововведениях.

Загрузка файлов на PHP

Начать следует с создания формы:

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

<input type="hidden" name="MAX_FILE_SIZE" value="5000000">

<input type='file' name='file[]' class='file-drop' id='file-drop' multiple required><br>

<input type='submit' value='Загрузить' >

</form>

<div class='message-div message-div_hidden' id='message-div'></div>

Для тега <form> мы указываются следующие атрибуты:

  • method — метод отправки данных (в нашем случае post);
  • action — путь к обработчику;
  • ectype — тип формы; ult значение multipart/form-data дает браузеру понять, что с ее помощью будут отправляться сразу несколько файлов.

Далее в форме создается скрытое поле MAX_FILE_SIZE , где указан максимальный объем загружаемых файлов в байтах. Поле должно предшествовать самому полю для выбора файлов. Это нужно для того, чтобы пользователь узнал, что превысил допустимый лимит на размер файла до того, как дождется загрузки 200-мегабайтного ролика. Но основная проверка все равно должна проводиться обработчиком, потому что значение этого поля можно изменить в браузере.

Во второй тег <input> добавляется атрибут multiple и имя file[] — это позволит с помощью одного поля загрузить сразу несколько файлов. Также в коде присутствует div, в который позже будет выводиться сообщение.

Далее указываются стили CSS:

.file-drop {

background:#fff;

margin:auto;

padding:200px 200px;

border:2px solid #333;

}

.file-drop_dragover {

border:2px dashed #333;

}

.file-drop__input {

border:0;

}

.message-div {

background:#fefefe;

border:2px solid #333;

color:#333;

width:350px;

height:150px;

position:fixed;

bottom:25px;

right:25px;

font-size:15px;

padding:5px;

z-index:99999;

box-shadow: 0 0 10px rgba(0,0,0,0.5);

}

.message-div_hidden {

right:-9999999999999999999px;

}

И вот как это выглядит:

Форма уже функционирует: можно выбирать или перетаскивать файлы, а после нажатия на кнопку «Загрузить» данные отправятся в обработчик. Там они попадают в многомерный супермассив $_FILES. Его структура выглядит так:

  • имя поля, через которое загружен файл;
  • name — имя загружаемого файла;
  • type — тип в формате MIME-type;
  • size — объем в байтах;
  • tmp_name — временный адрес;
  • error — номер ошибки, если она произошла.

Если через одно поле загружается сразу несколько файлов, то получение доступа к какому-то конкретному происходит следующим образом: $_FILES['file']['name'][0] — индекс файла находится в самом конце.

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

Вот как выглядит обработчик:

<?

if(isset($_FILES)) {

$allowedTypes = array('image/jpeg','image/png','image/gif');

$uploadDir = "files/"; //Директория загрузки. Если она не существует, обработчик не сможет загрузить файлы и выдаст ошибку

for($i = 0; $i < count($_FILES['file']['name']); $i++) { //Перебираем загруженные файлы

$uploadFile[$i] = $uploadDir . basename($_FILES['file']['name'][$i]);

$fileChecked[$i] = false;

echo $_FILES['file']['name'][$i]." | ".$_FILES['file']['type'][$i]." — ";

for($j = 0; $j < count($allowedTypes); $j++) { //Проверяем на соответствие допустимым форматам

if($_FILES['file']['type'][$i] == $allowedTypes[$j]) {

$fileChecked[$i] = true;

break;

}

}

if($fileChecked[$i]) { //Если формат допустим, перемещаем файл по указанному адресу

if(move_uploaded_file($_FILES['file']['tmp_name'][$i], $uploadFile[$i])) {

echo "Успешно загружен <br>";

} else {

echo "Ошибка ".$_FILES['file']['error'][$i]."<br>";

}

} else {

echo "Недопустимый формат <br>";

}

}

} else {

echo "Вы не прислали файл!" ;

}

?>

Если загрузка прошла успешно, создается массив разрешенных типов, по которому проверяется соответствие форматов. Затем, если валидация пройдена, с помощью функции move_uploaded_file файл перемещается из временного хранилища в указанную директорию.

Такой код хоть и работает, но он довольно примитивен и его нужно расширять:

  • удалить из названия специальные символы и пробелы, а в идеале вообще задать имя самостоятельно;
  • занести информацию в базу данных;
  • проверить размер файла;
  • если это изображение — его можно сжать и так далее.

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

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

Если добавить возможности JavaScript, форму можно сделать полезнее и красивее. Например, можно будет загружать файлы через AJAX, а также добавить анимацию при перетаскивании:

var fileDrop = document.getElementById('file-drop'); //Получаем объекты

var loadButton = document.getElementById('load-button');

var messageDiv = document.getElementById("message-div");

function dragOver() {//Меняем рамку, когда пользователь перетаскивает файл на поле

fileDrop.setAttribute('class','file-drop file-drop_dragover');

}

function dragOut() {//Возвращаем обычную рамку

fileDrop.setAttribute('class','file-drop');

}

function closeMessage() {//Закрываем сообщение

messageDiv.setAttribute('class','message-div message-div_hidden');

messageDiv.innerHTML = '';

}

function showMessage(data = 0) {//Показываем сообщение о том, что началась или закончилась загрузка

if(data == 0) {

data = "Загрузка...";

} else {

setTimeout(closeMessage,4000);

}

messageDiv.innerHTML = data;

messageDiv.setAttribute('class','message-div');

}

function uploadFile() { //Загружаем файл

dragOut();

var files = this.files;

var data = new FormData();

for(var i = 0; i < files.length; i++) { //Помещаем в дата массив с файлами

data.append(i, files[i]);

}

showMessage(); //Показываем сообщение, что загрузка началась

$.ajax({

url: "/file.php", //Ссылка на обработчик

type: 'POST', //Метод передачи

data: data, //Массив с файлами

cache: false, //Обязательно указать false

processData: false, //Обязательно указать false

contentType: false, // Обязательно указать false

success: function (data) {//В случае успеха показываем сообщение с результатом работы и очищаем поле

showMessage(data);

fileDrop.value = null;

}

});

}

//Указываем события для вызова функций

fileDrop.addEventListener('dragover',dragOver);

fileDrop.addEventListener('dragleave',dragOut);

fileDrop.addEventListener('change',uploadFile); //Отправляем файлы сразу после того, как они будут выбраны

Сначала поле передается в объект fileDrop, а затем создаются функции dragOver, dragOut, showMessage и closeMessage: первые две меняют рамку для тега <input>, а вторые показывают и скрывают сообщение. Затем создается функция uploadFile, которая отправляет файл через AJAX.

Функция получает файлы с помощью объекта FormData, затем показывает сообщение, что началась загрузка, и начинает отправлять файл.

Из-за измененного способа передачи структура супермассива $_FILES немного меняется: теперь сначала указывается индекс файла, а потом — необходимая ячейка.

<?

if(isset($_FILES)) {

$uploadDir = "files/";

$allowedTypes = array('image/jpeg','image/png','image/gif');

for($i = 0; $i < count($_FILES); $i++) {

$uploadFile[$i] = $uploadDir . basename($_FILES[$i]['name']);

$fileChecked[$i] = false;

echo $_FILES[$i]['name']." | ".$_FILES[$i]['type']." — ";

for($j = 0; $j < count($allowedTypes); $j++) {

if($_FILES[$i]['type'] == $allowedTypes[$j]) {

$fileChecked[$i] = true;

break;

}

}

if($fileChecked[$i]) {

if(move_uploaded_file($_FILES[$i]['tmp_name'], $uploadFile[$i])) {

echo "Успешно загружен <br>";

} else {

echo "Ошибка ".$_FILES[$i]['error']."<br>";

}

} else {

echo "Недопустимый формат <br>";

}

}

} else {

echo "Вы не прислали файл!";

}

?>

Вот как работает загрузка файлов на AJAX:

Заключение

Дальше с файлами можно делать все что угодно:

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

Это лишь малая часть возможностей PHP — подробнее узнать о них всех можно из курса «PHP-разработчик», в котором сильная теоретическая база закрепляется сложными, но интересными практическими задачами.

Курс «PHP-разработчик»
Обширная программа для изучения PHP, состоящая из 4 курсов. Поможет с нуля овладеть популярным языком программирования, устроиться в IT-компанию вашей мечты или стать независимым разработчиком.
  • Живая обратная связь с преподавателями
  • Неограниченный доступ к материалам курса
  • Стажировка в компаниях-партнёрах
  • Дипломный проект от реального заказчика
  • Гарантия трудоустройства в компании-партнёры для выпускников, защитивших дипломные работы
Хочешь получать крутые статьи по программированию?
Подпишись на рассылку Skillbox