В наше время веб-сайты становятся все более интерактивными. Это касается не только специализированных сервисов, но и обычных интернет магазинов, блогов и небольших сайтов. Основной особенностью является асинхронный JavaScript и XML сокращенно AJAX. Эта технология позволяет браузеру в фоновом режиме общаться с веб-сервером и при обновлении данных, веб-страница не перезагружается полностью. Другими словами, мы можем делать запросы и получать ответы от сервера не перезагружая страницу в браузере.
Поскольку наиболее популярным языком для разработки веб-приложений является PHP, то сегодня мы будем использовать связку AJAX и PHP. Пример будет хорош для понимания основных принципов работы с AJAX и PHP.
На самом деле особых сложностей быть не должно, алгоритм действий:
- Выбрать картинку
- Нажать кнопку “Отправить”
- Перехватить вызов формы с помощью JavaScript (jQuery)
- Передать содержимое в специальный php скрипт-обработчик
- Вернуть результат выполнения
- Обработать результат при помощи JavaScript (jQuery)
- Вывести пользователю информацию о загрузке
Кратко о jQuery и AJAX
Немного отклонюсь от темы и объясню что такое jQuery. jQuery — это специальная JavaScript библиотека, которая помогает упростить разработку веб приложений в несколько раз, также данная библиотека предоставляет API для работы с AJAX. Простыми словами, мы напишем меньше кода, чем если бы это делали на чистом JS.
Ajax позволяет не перезагружая веб-страницу, обмениваться данными с веб-сервером и обновлять ее содержимое.
Я склоняюсь к тому, что если есть инструмент, который позволяет вам ускорить разработку без последствий, то почему бы его не использовать? Но чистый JS тоже не помешало бы знать(хоть и лично мой уровень владения JS равен уровню копипаста примеров со stackoverflow 🙂 ).
Мы разберем одну из проблем, которую мне однажды пришлось решать, а именно — загрузка изображения на сайт с предварительным просмотром. Если вы меняли аватарку в вконтакте, вы понимаете о чем я пишу.
Нам понадобится 3 простых файла, это:
- Страница с формой
- php обработчик
- файл js
index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Ajax Upload</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link rel="stylesheet" href="main.css"> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script type="text/javascript" src="ajaxupload.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h1>Image upload</h1> <div class="image-preview"> <img id="preview" src="" alt=""> </div> <form id="upload-image" enctype="multipart/form-data"> <div class="form-group"> <label for="image">Image file:</label> <input type="file" name="image" id="image"> </div> <input type="submit" class="btn btn-default"> </form> <div id="result"> </div> </div> </div> </div> </body> </html>
Обычная html страница с формой. Обратите внимание на enctype="multipart/form-data"
, это нужно для передачи файлов, параметр указывает на способ кодирования данных. Если передаете файлы, значение всегда должно быть multipart/form-data
.
handler.php
// Проверяем установлен ли массив файлов и массив с переданными данными if(isset($_FILES) && isset($_FILES['image'])) { //Переданный массив сохраняем в переменной $image = $_FILES['image']; // Проверяем размер файла и если он превышает заданный размер // завершаем выполнение скрипта и выводим ошибку if ($image['size'] > 200000) { die('error'); } // Достаем формат изображения $imageFormat = explode('.', $image['name']); $imageFormat = $imageFormat[1]; // Генерируем новое имя для изображения. Можно сохранить и со старым // но это не рекомендуется делать $imageFullName = './images/' . hash('crc32',time()) . '.' . $imageFormat; // Сохраняем тип изображения в переменную $imageType = $image['type']; // Сверяем доступные форматы изображений, если изображение соответствует, // копируем изображение в папку images if ($imageType == 'image/jpeg' || $imageType == 'image/png') { if (move_uploaded_file($image['tmp_name'],$imageFullName)) { echo 'success'; } else { echo 'error'; } } }
Это очень упрощенный обработчик. Имя картинки я сгенерировал использовав функцию hash
. Хорошей практикой считается изменять имена файлов при загрузке их на сервер.
ajaxupload.js
$(document).ready(function () { function readImage ( input ) { if (input.files && input.files[0]) { var reader = new FileReader(); reader.onload = function (e) { $('#preview').attr('src', e.target.result); } reader.readAsDataURL(input.files[0]); } } function printMessage(destination, msg) { $(destination).removeClass(); if (msg == 'success') { $(destination).addClass('alert alert-success').text('Файл успешно загружен.'); } if (msg == 'error') { $(destination).addClass('alert alert-danger').text('Произошла ошибка при загрузке файла.'); } } $('#image').change(function(){ readImage(this); }); $('#upload-image').on('submit',(function(e) { e.preventDefault(); var formData = new FormData(this); $.ajax({ type:'POST', // Тип запроса url: 'handler.php', // Скрипт обработчика data: formData, // Данные которые мы передаем cache:false, // В запросах POST отключено по умолчанию, но перестрахуемся contentType: false, // Тип кодирования данных мы задали в форме, это отключим processData: false, // Отключаем, так как передаем файл success:function(data){ printMessage('#result', data); }, error:function(data){ console.log(data); } }); })); });
В этом скрипте происходит самое интересное. При помощи функции readImage()
мы будем считывать файл с поля формы и передавать его в блок для предварительного просмотра. Создается объект FileReader
. Он позволяет веб-приложению считывать содержимое файла на компьютере пользователя. Событие .onload
сработает когда содержимое будет считано, при помощи этого события мы выведем изображение в блок предварительного просмотра.
И напоследок, метод .readAsDataURL()
запускает процесс чтения файла, по завершению чтения будет выполнено событие .onload
и картинка появится у вас на экране.
Функция printMessage
создана для вывода информации об успешной или провалившейся попытке загрузки файла. Подробно не рассматриваем, ничего особенного не представляет.
Перехват формы и её обработка. При клике на кнопку «Отправить» событие будет перехвачено скриптом и при помощи функции .preventDefault()
форма не отправит данные в index.html
. .preventDefault()
служит для отмены вызова каких-либо событий.
Объект FormData
нужен нам для создания POST
запроса к нашему скрипту, это намного проще чем вписывать каждый элемент формы в строку. Создали объект, заполнили данными, отдали в наш ajax
.
Ну и собственно сам запрос AJAX. Поскольку мы используем библиотеку jQuery, составить и выполнить такой запрос не вызовет у вас никаких проблем.
Собственно, на этом и закончим. Изображение загружается, страница не перезагружается, все довольны. Если у вас возникают вопросы или предложения, пишите комментарии.
Хорошего дня и успехов 🙂
Здравствуйте! А ссылку можно где это реализовано? Посмотреть бы. Спасибо за ранее.
Ничего не работает
Какие ошибки при выполнении появляются?
Большая ошибка делать так:
$imageFormat = explode(‘.’, $image[‘name’]);
$imageFormat = $imageFormat[1];
В этом случае на сервер можно загрузить любой файл под видом картинки. Скрипт возьмёт только первый элемент массива «imageFormat». Здесь можно будет манипулировать такими значениями как fileName.js.jpg или FileName.php.jpg. При этом, на сервер будет загружен файл с расширением «JS» или чего хуже «PHP». При загрузки PHP файла, с сервером можно будет делать, всё что угодно.
Следует делать так:
$imageFormat = explode(‘.’, $image[‘name’]);
$imageFormat = array_pop($imageFormat);
Всё верно, в идеале еще и проверять
mime type
или использовать какой-то пакет для валидации данных.Спасибо большое. Это то, что я искала. Ваш код работает безупречно. Но еще есть одна большая проблема. Не загружаются на сайт фотки из галереи смартфона в Яндексе или стандартном браузере. В Google такой проблемы нет. Причем нет и ответа на все проверки по типу и наличия временного файла, ни по размеру. Нигде не могу найти решение этой проблемы. Помогите.
Добрый день, Александр. Могу я Вас попросить пояснить как измениться JS-скрипт, если в форме 3 input типа file. И я хочу передать три изображение одной формы.
Я читал, что для этого нужен цикл и последовательно передавать каждый файл, но вот как это реализовать не пойму. Буду благодарен за ответ. Спасибо!
Если 3 input, то им нужно просто дать разные имена или массив. Если имена разные, то обрабатываем каждое поле, если массив — просто обрабатываем через
foreach
Александр. А как сделать, чтобы картинка не обновлялась на экране сразу после загрузки её кнопкой «обзор» (type=»file»), а обновлялась только после отправки формы нажатием на кнопку отправки формы (type=»submit»). Проблема в том, что посетители увидев подгруженную кнопкой «обзор» картинку думают, что она уже сохранена и забывают делать отправку формы, и картинка не сохраняется на сайте. Заранее спасибо за ответ.
Вызвать функцию
readImage
после успешного ответа от сервера.Александр. Не получается сделать. Не могли бы вы подсказать для человека с начальными знаниями, какие для этого нужно внести изменения в ваш программный код? Спасибо.
Добрый день. Напишите сообщение странице на фейсбуке https://www.facebook.com/adminnotebook. Там с вами обсудим детали)
Всё получилось отлично! Подскажите как сделать загрузку 4 файлов?
использовать мультизагрузку — https://www.w3schools.com/Tags/att_input_multiple.asp
В самом обработчике (php) перебирать каждый елемент массива и сохранять
Александр, Вы дали отличный, чистый, расширяемый код. Я, например, сделал проверку загружаемого файла более строгой, но вопрос не в этом.
На просторах инета можно найти массу вариантов ajax-загрузки файлов, но.. все эти варианты реализуются для одиночной формы. А что делать если этих абсолютно одинаковых форм неопределенно много? Вот в Вашем примере, если задублировать HTML форму и оставить без изменения JavaScript и php-обработчик, то не будет работать ни одна форма.
А у меня задачка посложнее, я знаю, что ее решение реализовано, но не могу найти хотя бы завалящего примера.
Задача в том, что я вывожу формы в цикле (обычный каталог товаров). В форме нужно изменять не только текстовые поля, но и заменять аватар товара. Замену я пытался сделать в Вашем стиле и… не работает ни для одной формы. По кнопке «Выбрать файл» название выбранного появляется рядом с кнопкой и все. Как можно решить эту задачу?
Можно воспользоваться функцией
.index()
— https://api.jquery.com/index/Выбирать по индексу элемент и внутри него уже отображать картинку, если нужен пример, могу попробовать сделать
Не загружаются на сайт фотки из галереи смартфона в Google . В Яндексе такой проблемы нет.
максимально допустимые размеры файла в порядке. Кто подскажет? Пожалуйста!
[email protected]
Не могли бы вы отправить уже готовый рабочий набор файлов пхп хтмл джс на почту, с поддержкой загрузки нескольких файлов сразу?
Благодарю за шаблон скрипта. Всё работает.
Добрый день!
Скрипт работает, фотки записываются или не записываются при нарушении. Только вот сообщений вообще никаких не поступает. В чем может быть дело?