Загрузка файлов или картинок на сервер является довольно типичной задачей. Но прогресс не стоит на месте и поэтому сейчас конечно же хочется, чтобы загрузка файлов происходила в фоновом режиме. Как правило ранее это можно было реализовать с использованием технологии flash либо iframe. Также многие используют плагины такие как jQuery Form Plugin или Ajax File Upload Plugin или Multiple File Upload Plugin и море других. С появлением объекта FormData все стало значительно проще. FormData() позволяет составить набор данных для отправки на сервер с помощью XMLHttpRequest.
Давайте же попробуем написать свой код без всяких плагинов (ну кроме конечно фреймворка jQuery) для загрузки картинок или файлов на сервер в фоновом режиме. Вообще алгоритм наших действий будет примерно таков: заполняем поля формы данными. Поля могут быть какими угодно, и текст, и текстареа и селект и файлы. При выборе файлов, благодаря нашему коду на jQuery, эти файлы в фоновом режиме будут загружены во временную директорию на сервере, например в “tmp”. Далее при нажатии на кнопку submit формы, данные отправляются серверному скрипту, который эти данные обработает. Представим что это статьи. Переданные данные мы запишем в базу данных с уникальным id. Далее создадим в каталоге “images” директорию c уникальным номером “id” и если в папке “tmp” у нас были какие то файлы мы их скопируем в созданную папку “id” после чего очистим папку “tmp”. Резюмируя: фоном заливаем картинки в tmp, при сабмите формы данные записываем в базу, у нас появлется уникальный номер записи. Создаем папку с этим номером и перемещаем туда наши файлы. Все. В данной статье заливку в базу и копирование файлов мы рассматривать не будем. Думаю тут у каждого будет что-то свое. Мы сосредоточимся на одном – асинхронной загрузке картинок (или файлов).
Итак вот наш html кусок. Тут обратим внимание на то, что у нас есть гиф файл с картинкой прелоудером (зацикленный кружок), который мы стилями прячем от показа. Также полю file присвоим id = file, а форме enctype=”multipart/form-data”. Имя поля file будет file[] т.е. чтобы мы могли работать с массивом, раз у нас разрешена загрузка нескольких файлов (атрибут multiple).
<!-- index.php --> <meta charset="utf-8" /> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <style> #preloader {visibility: hidden;} </style> <h2>Добавить информацию</h2> <form action="" enctype="multipart/form-data" method="POST"> <input type="text" name="name" placeholder="Название" /> <input type="text" name="description" placeholder="Описание" /> <input id="file" type="file" multiple="multiple" name="file[]" /> <div id="preloader"><img alt="loader" src="preloader.gif" /></div> <div id="info"></div><!-- сюда будет выводится информация о заливке --> <input type="submit" name="add" value="Добавить" /> </form>
В данной форме у нас помимо поля с файлом есть еще пара полей для примера: input=text. Т.е. перед нами обычная форма например для админки, которая представляет собой набор необходимых вам полей. Для начала если хотите можете проверить работу скрипта прописав вначале файла строки показа массива FILES:
//upload.php print_r($_FILES);
Теперь напишем наш серверный скрипт, который будет вызываться с помощью jQuery. Его задача перекинуть залитые файлы из временной директории сервера в нашу, допустим как мы решили в “tmp”, после чего показать их.
//upload.php function show_dir($dir) // функция показа картинок из tmp папки { $list = scandir($dir); unset($list[0],$list[1]); foreach ($list as $file) { echo "<img style="height: 150px;" alt="" src="./$dir$file" /> "; } } foreach ($_FILES as $key => $value) { //перемещение файлов в tmp move_uploaded_file($value['tmp_name'], "tmp/".$value['name']); } show_dir('./tmp/');
А теперь и наш js-скрипт, который в фоновом режиме зальет наши файлы на сервер. Все волшебство будет выполнено благодаря объекту FormData(). Этот код мы допишем в конец нашего index.php перед тегом.
<script> $(document).ready(function(){ $('#preloader').hide(); $('#file').bind('change', function(){ var data = new FormData(); var error = ''; jQuery.each($('#file')[0].files, function(i, file) { if(file.name.length < 1) { error = error + ' Файл имеет неправильный размер! '; } //Проверка на длину имени if(file.size > 1000000) { error = error + ' File ' + file.name + ' is to big.'; } //Проверка размера файла if(file.type != 'image/png' && file.type != 'image/jpg' && !file.type != 'image/gif' && file.type != 'image/jpeg' ) { error = error + 'File ' + file.name + ' doesnt match png, jpg or gif'; } //Проверка типа файлов data.append('file-'+i, file); }); if (error != '') {$('#info').html(error);} else { $.ajax({ url: 'upload.php', data: data, cache: false, contentType: false, processData: false, type: 'POST', beforeSend: function() { $('#preloader').show(); }, success: function(data){ $('#info').html(data); $('#preloader').hide(); } }); } }) }); </script>
Ну вот вроде бы и все. Если кто-то что не понял, спрашивайте. Если у кого то будут дополнения, тоже буду рад!
Совет: если вы еще не использовали код для удаления файлов из какой-либо директории, то рекомендую для теста поменять функцию удаления rmdir на echo чтобы убедиться что будут удалены только те файлы которые вы хотите удалить. Gif прелоадеры можно взять например из моего урока Как сделать Gif анимацию. Примеры в конце статьи.
UPD:
Если кому захочется добавить красоты, например прогресс бар, то для этого нам надо будет дописать несколько строк кода. В html шаблон мы добавим супер элемент из html5 – progress, а в js код, добавим несколько строк с объектом XMLHttpRequest.
И так, наш html дополнится следующим:
<progress></progress>
А в код js допишем:
function progressHandlingFunction(e){ if(e.lengthComputable){ $('progress').attr({value:e.loaded,max:e.total}); } }
и
xhr: function() { var myXhr = $.ajaxSettings.xhr(); if(myXhr.upload){ // проверка что осуществляется upload myXhr.upload.addEventListener('progress',progressHandlingFunction, false); //передача в функцию значений } return myXhr; }
Финальный результат js кода:
<script> $(document).ready(function(){ function progressHandlingFunction(e){ if(e.lengthComputable){ $('progress').attr({value:e.loaded,max:e.total}); } } $('#preloader').hide(); $('#file').bind('change', function(){ var data = new FormData(); var error = ''; jQuery.each($('#file')[0].files, function(i, file) { if(file.name.length < 1) { error = error + ' Файл имеет неправильный размер! '; } if(file.size > 1000000) { error = error + ' File ' + file.name + ' is to big.'; } if(file.type != 'image/png' && file.type != 'image/jpg' && !file.type != 'image/gif' && file.type != 'image/jpeg' ) { error = error + 'File ' + file.name + ' doesnt match png, jpg or gif'; } data.append('file-'+i, file); }); if (error != '') {$('#info').html(error);} else { $.ajax({ url: 'productUploadImg.php', type: 'POST', xhr: function() { var myXhr = $.ajaxSettings.xhr(); if(myXhr.upload){ // проверка что осуществляется upload myXhr.upload.addEventListener('progress',progressHandlingFunction, false); //передача в функцию значений } return myXhr; }, data: data, cache: false, contentType: false, processData: false, beforeSend: function() { $('#preloader').show(); }, success: function(data){ $('#info').html(data); $('#preloader').hide(); } , error: errorHandler = function() { $('#info').html('Ошибка загрузки файлов'); } }); } }) }); </script>
UPD2: Для тех кто хочет оформить прогресс бар более красиво, может посмотреть статью по оформлению элемента прогресс на css, а в этой статье можно посмотреть на вдохновляющие примеры внешнего вида прогресс бара.
Ну пример что в лом было сделать?
Все здорово!
Но вот примера точно не хватает!
Будет свободная минутка, обязательно сделаю.
Код нерабочий, содержит ошибки, пришлось его перелапатить чтобы заставить работать. Автору было бы не плохо сначала проверить его прежде чем выкладывать.
Такой вопрос, а если заливка файлов будет происходить одновременно несколькими пользователями? Тогда единая для всех папка tmp не подойдет, нужна уникальная для каждого пользователя. Тогда следующий вопрос, где хранить имя для этой папки и как его использовать?
Это я писал для админки, но если юзеров несколько, то сходу вот подумалось, что надо это дело отслеживать через сессию. При логине у нас вроде создается уникальный sission_id если не ошибаюсь, если нет то заведем ее. А php скрипт который перемещает файлы в tmp будет перемещать их в директорию с именем sission_id в папке tmp. Как то так я думаю…
В каком месте не рабочий? Я им переодически пользуюсь
А есть вариант для одного файла? А то никак не могу разобраться.
Так и что там с примером-то?
Полгода прошло однако)
Здравствуйте, я хотел бы узнать о том как могут пользователи приходящие ко мне на сайт, загрузить на сервер фотографию, т.е. просто загрузить не получая ни каких ссылок на скачивание.
Охаха) Я думал все уже разобрались и разбежались) Вобщем записал в тодо, в ближайшее время сделаю, на этот раз точно!
Так тут совсем все просто. Сделайте простую форму и решите для себя в какую директорию будут заливаться фотки…
а можно ли как нить зделать так, чтоб фотки заливались не на сервер а ко мне на комп? и какая фотра для этого нужна?
Отличная статья, огромное спасибо. Только в IE-8 загрузка не работает. В чем причина?
Спасибо,хороший рабочий скрипт и главное он гибкий, то есть, при желании, можно доработать под себя под любые нужды.
..скрипт в принципе рабочий..
http://habrahabr.ru/post/148999/
но..
использование его как “есть”..
принесет в дальнейшем проблемы.. в нем не задействованы элементарные проверки от инъекций..
Ну ты *** ни хрена у тебя ни работает php функция, с***л код и выложил у себя на сайте, сам не понимая что в нем написано.
Женек, ты просто лошара
Класс!!! искал то что нужно) только вот такой вопрос: как прогресс добавить к каждой картинке отдельно? я так понимаю в цикле нужно да? если можно накидай пример небольшой.
а почему нельзя сделать так?
if(isset($_FILES)){
$file = $_FILES;
foreach ($file as $key => $value) {
$config['upload_path'] = './'.$lager.'/';
$config['allowed_types'] = 'jpeg|jpg|png';
$config['max_size'] = 5000;
//$config['encrypt_name'] = TRUE;
$_FILES[0] = $file[$key];
$this->load->library('upload', $config);
$this->upload->initialize($config);
$this->upload->do_upload(0);
$data = $this->upload->data();
}
}