Про загрузку файлов на сервер я писал здесь. Сегодня я расскажу про асинхронную загрузку файлов на сервер.
В настоящее время довольно популярным решением для веб-сайтов является работа пользователя со страницей без ее перезагрузки. В большинстве своём это делается с помощью Ajax
– технологии асинхронного взаимодействия с сервером, основанной на объекте XMLHttpRequest
.
В этой статье рассмотрим простое решение одной из самых распространенных задач – асинхронная загрузка файла на сервер при помощи PHP. Задача будет следующая: На странице (предположим, что это страница в личном кабинете пользователя) есть форма с input
типа file
и кнопка Отправить
. Ниже находится контейнер с картинкой (аватар пользователя по задумке) в который асинхронно подгружается картинка после выполнения AJAX запроса. И по умолчанию она просто выводится из базы данных. То есть, при загрузке картинки новое сгенерированное название будет записываться в БД, сама картинка под этим названием загружаться на сервер и выводится в контейнере взамен предыдущей. И всё это асинхронно.
Сразу скажу, что я по бОльшей степени преследовал цель описать сам процесс загрузки, особо не сосредотачиваясь на "элегантности" кода ). Кто как захочет использовать этот код. Кто-то кусочек нужный возьмёт, кто-то целый класс напишет на его основе, кто-то переделает для себя, кто-то вовсе не будет его использовать никак. Тем более, там действительно, нужна доработка под боевые задачи. Например, валидацию на MIME-типы (и другие проверки) нужно делать ещё в JS до попадания данных скрипту PHP. В скрипт данные должны приходить отвалидированные. Или при загрузке картинки удалять на сервере старую картинку. Поэтому я решил, что полезнее будет подробно описать процесс, нежели допиливать до идеала то, что каждый для себя доработает как нужно. Поехали.
Безусловно, подключаем jQuery (если ещё в проекте нигде данная библиотека не подключена):
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
Простая разметка HTML формы и картинка из базы. Предположим, что у вас уже есть база данных и в базе уже есть таблица (например, user
). В блоке с id="photo-content"
выводится картинка из базы. Я весь этот код помещу в файле index.php
.
<?
$db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$query = "SELECT `avatar` FROM `user` WHERE `id` = ?";
$id = 1;
$stmt = $db->prepare($query);
$stmt->execute([$id]);
$image = $stmt->fetchColumn();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Ajax Upload</title>
<link rel="stylesheet" href="main.css">
</head>
<body>
<div id="wrapper">
<h1>Image upload</h1>
<form method="post" enctype="multipart/form-data" id="form-file-ajax" action="upload.php">
<input type="file" id="file" name="file" required>
<br/>
<button type="submit" id="btn-file-upload">Загрузить</button>
<!-- preloader.gif - картинка имитирующая процесс загрузки -->
<div id="process"><img src="preloader.gif" alt="Loading"></div>
<div id="photo-content">
<!-- Эта картинка выводится из базы по умолчанию -->
<img src="http://site.com/upload/<?= $image ?>" alt="Image" width="400">
</div>
</form>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<!-- Этот файл будет содержать код отправки данных PHP скрипту -->
<script src="ajax.js"></script>
</div>
</body>
</html>
Немного CSS стилей:
#wrapper{
width: 60%;
margin: 20px auto;
}
form button{
margin-bottom: 50px;
}
input[type=text],
input[type=file]{
margin-bottom: 20px;
}
#process {
display: none;
}
В файле ajax.js
:
$(document).ready(function(){
$("#form-file-ajax").on('submit', function(e){
e.preventDefault();
var formData = new FormData();
var form = $(this);
formData.append('file', $('#file').prop("files")[0]);
$.ajax({
url: form.attr('action'),
type: form.attr('method'),
processData: false,
contentType: false,
cache:false,
dataType : 'text',
data: formData,
// Будет вызвана перед осуществлением AJAX запроса
beforeSend: function(){
$('#process').fadeIn();
},
// будет вызвана после завершения ajax-запроса
// (вызывается позднее функций-обработчиков успешного (success) или аварийного (error)
complete: function () {
$('#process').fadeOut();
},
success: function(data){
//form[0].reset();
data = JSON.parse(data);
var image = '<div class="img-item"><img src="http://site.com/upload/'+data.file+'" width="400"></div>';
var photoContent = $("#photo-content");
photoContent.html('');
photoContent.append(image);
},
error: function(data){
console.log(data);
}
});
});
});
В файле upload.php
:
<?php
if(isset($_FILES['file'])) {
if ($_FILES['file']['name'] !== '' && $_FILES['file']['error'] == 0) {
try {
// MIME-типы нужно проверять ещё в JS коде и выводить ошибки пользователю
// Сейчас они вываливаются во вкладке "Network" браузера
$fileTmpName = $_FILES['file']['tmp_name'];
$fi = finfo_open(FILEINFO_MIME_TYPE);
$mime = (string) finfo_file($fi, $fileTmpName);
if (strpos($mime, 'image') === false) die('Можно загружать только изображения с расширениями .jpg, .jpeg, .png!');
$image = getimagesize($fileTmpName);
$extension = image_type_to_extension($image[2]);
$name = randomFileName($extension);
$file = $name.str_replace('jpeg', 'jpg', $extension);
if (!move_uploaded_file($fileTmpName, __DIR__ . '/upload/'.$file)) {
throw new Exception('При загрузке изображения произошла ошибка на сервере!');
}
// Записать имя файла в БД
$db = new PDO('mysql:host=localhost;dbname=ajax', 'root', 'password');
$user_id = 1;
$query = "UPDATE `user` SET `avatar` = :avatar WHERE `id` = :user_id";
$params = [':avatar' => $file, ':user_id' => $user_id];
$stmt = $db->prepare($query);
if (!$stmt->execute($params)) {
throw new Exception('Произошла ошибка при записи в БД!');
}
// Записать в $data имя файла
$data = ['file' => $file];
echo json_encode($data);
} catch (Exception $e) {
die($e->getMessage());
}
}
}
// Генерируем уникальное имя для файла
function randomFileName($extension = false)
{
$extension = $extension ? '.' . $extension : '';
do {
$name = md5(microtime() . rand(0, 9999));
$file = $name . $extension;
} while (file_exists($file));
return $file;
}
Вот и все. Ясное дело, что это не идеальное решение и как использовать его (если использовать) решать каждому самостоятельно. Может быть в дальнейшем я напишу специальный класс со всеми недочётами. А пока, как-то так.
Для работы данного примера без ошибок не забудьте заменить все адреса своими и указать корректные доступы к базе данных!
Комментарии (0)
Пока еще не было комментариев ✍️