Как стать автором
Обновить

Комментарии 57

Очень вовремя! Вот только собирался использовать мультиаплоад в своем проекте и вот замечательная статья.
Довольно четко и очень понятно объяснено.
Огромное спасибо.
на здоровие ;)
Как вы собираетесь реализовать поддержку старых браузеров?
забить?)
НЛО прилетело и опубликовало эту надпись здесь
Эх, жаль что это можно будет использовать только года через 3-4 когда умрут все браузеры которые не понимают html5
Не скажите, смотря для каких целей. Например, в данном случае я делал интерфейс для одной почтенной женщины-фотографа, она использует (а если бы и не использовала, то можно было бы убедить) файрфокс и ее все устраивает.

Или вот пример с gmail — они же отказались от поддержки IE6, хотя его до сих пор используют 15 (или даже 20) % пользователей, которые получают предупреждение, что их браузер устарел.

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

Но это я так. Согласен, чтоэту технологию пока рано запускать в массы. Как минимум надо дождаться, когда все последние версии браузеров будут ее поддерживать.
Зря Вы так. Для массового пользователя — да, а вот для своего удобства — уже сейчас.
Мало по малому, но уже можно начинать внедрять
К сожалению в реальных проектах придется использовать еще и альтернативный вариант загрузки, пока такой функционал не будут поддерживать основные броузеры :(
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Расстроило конечно, что File API не поддерживает IE9 Beta: это странно, учитывая что разработчики IE сейчас взяли курс на обильную поддержку html 5. Но как бы то ни было, очевидно, что в будущем всем браузерам придется подтянуться.
Ничего странного, все просто. File API — черновик, его еще будут дописывать и переписывать. Если бы в IE9 внедрили FileAPI в текущем виде, то ничего хорошего не вышло бы. А мозиловцы вообще любят всякую экспериментальщину внедрять. Отсюда и остался в FF 3.6 метод sendAsBinary. В 4-ке уже должен быть FormData, который и описывается в последней версии черновика.
Согласен, весь HTML5 пока черновик, но ничего плохого в том, если бы ИЕ поддерживал уже тоже не было бы. Вообще, остальные браузеры регулярно обновляют младшие версии (может, подверсии? как правильно?), добавляя новые возможности. А уж если в ИЕ какой-то возможности нет, то еще год-другой надо ждать, пока выйдет следующий.
Вот в этом-то и проблема. Если Хром, FF, Опера обновляются достаточно часто, то IE — крайне редко. Поэтому внедрять что-то экспериментальное в IE опасно, т.к. может так случиться, что через неделю после релиза черновик перепишут, выпустят новые версии браузеров и… IE останется не у дел, со старыми реализациями (и снова хаки для IE). Разработчики оперы тоже не любят экспериментальные фичи. Вспомнить хотя бы border-radius (и многие другие css3 свойства), реализовывать которые Opera Software не спешили.

А вообще, реализовывать такой функционал следует только при уверенности, что Вы сможете среагировать на изменение стандарта. Если Вы работаете над сайтом, который Вы точно будете поддерживать через полгода-год, то не страшно. А если Вы работаете над заказом, то лучше на это не рассчитывать. Иначе вместе с очередным обновлением браузеров может умереть весь новый функционал, а исполнителя и след простыл.
Ну ок, убедили — хорошо, что ИЕ пока не поддерживает :)
По моему IE как был не удел так и будет.
Если Хром, FF, Опера обновляются достаточно часто, то IE — крайне редко
а так нечестно, да
Для загрузки картинок (небольших файлов) ваш код условно подходит.
По-нормальному, надо без извращений с ручным формированием тела запроса использовать FormData
правильно, но к сож. файрфокс пока не поддерживает. Выйдет официальо 4й — переделаем!
Переписал код так, чтоб где это возможно использовался FormData. Если интересно, см. апдейт внизу статьи.
вовремя… надо бы jquery plugin заделать
сделаете? покажете?
искал добровольцев :)
Сделано! Если интересно, см. апдейт к статье.
Спасибо! Применю на своем проекте, как раз не будет лишним)

и да здравствует HTML5 в W3C
www.w3.org/TR/FileAPI/ — текущая спецификация File API на сайте W3C

Для скрытия инпута можно просто сделать ему visibility:hidden и растянуть поверх графического элемента
М… меня опередили *сворачивает черновик* =)
Последний год использовал www.plupload.com/.
Опенсурс библиотека для загрузки файлов. Поставил на сайт — если у пользователя оказывался не хтмл5-браузер, автоматически библиотека переключала на другой способ загрузки.
Апи очень простое, рекомендую.
Спасибо за линк! Это громоздкое, но зато комплексное решение по загрузке файлов. И, насколько я понял, оно уже использует html 5 там, где это возможно
Да, так и есть. Более того, через апи самому можно выставлять приоритет способов загрузки. Я ранее ставил так: gears -> html5 -> flash -> silverlight -> html4.
ок, спс за инфо. Я гуглил готовые решения перед тем как сделать свой вариант, но на это не натыкался.
Спасибо
вместо $('ul#img-list') используйте $('#img-list'). Во втором случае получим элемент намного быстрей, так как будет использоваться родной document.getElementById
спасибо за инфо. Это, знаете ли, просто привычка, чтоб в коде видеть, какой именно элемент имеет такой айди (надо в комментариях писать — никак не могу переучиться). Потом везде убрал, а тут забыл.
Сегодня весь вечер внедрял загрузку по этому посту и нашел очень неприятную штуковину.
В частности, этот код:
if (self.xhr.sendAsBinary) {
    // firefox
    self.xhr.sendAsBinary(body);
} else {
    // chrome (W3C spec.)
    self.xhr.send(body);
}

> метод send() объекта XMLHttpRequest может принимать в параметре бинарные данные, что успешно и реализовано в Google Chrome
Попробовал на chrome 7 и trunk chromium 9 — они не смогли. Сейчас думаю о возможных решениях проблемы, первое, что пришло на ум — это реализация метода sendAsBinary с помощью FileWriter API. Это апи разработчики совсем недавно добавили в хромиум и firefox. Вот код:
if (XMLHttpRequest.prototype.sendAsBinary === undefined && Uint8Array) {
    XMLHttpRequest.prototype.sendAsBinary = function(data) {
        var blob = new BlobBuilder(),
            arrb = new ArrayBuffer(data.length),
            ui8a = new Uint8Array(arrb, 0);
        for (var i=0; i<data.length; i++) {
            ui8a[i] = (data.charCodeAt(i) & 0xff);
        }
        blob.append(arrb);
        var blob = blob.getBlob();
        this.send(blob);
    }
}
А если взять reader.readAsDataURL,
отсечь от начала «data:image/png;base64,»
разбить оставшися текст на строки по 74 символов каждая (включая, \r\n),
Поставить хэдер Content-Transfer-Encoding: base64

и отправить?
А почему по 72 именно?
74*
просто я отправил из почтовой программы Thunderbird себе письмо с вложенным файлом.
Потом я посмотрел исходный код письма.
Так вот, Thunderbird закодировал файл в строку base64 и после каждого 72 символа он ставил
\r\n (либо просто \n)
Из-за этого подумал, что 74 — это максимальная длина строки в письмах.
===
P.S. Но вот сейчас взял первое попавшееся письмо на gmail, выбрал «Показать оригинал».
Увидел, что там в строке base64 76 печатных символов.

В rfc искал чего-то ничего по этому поводу не нашёл.

Хорошая статья, спасибо.
Но у способа есть минус: большие файлы загружаются в память, браузер висит и отжирает много памяти.
Как справедливо отметил выше товарищ Demetros, для этой задачи лучше использовать объект FormData, который будет реализован в файрфоксе (см. описание на сайте MDC), начиная с версии 4 (в хроме уже есть, про остальные не в курсе). Очень вероятно, что браузер тогда будет кушать гораздо меньше памяти, ибо возьмет на себя всю черновую работу по чтению файлов.

Пока же можно ограничивать кол-во одновременных загрузок и максимальный размер одного файла.
вот метод FormData:
void append(DOMString name, Blob value);

и чего? value что должно содержать? не тело ли файла? раз у неё тип blob

Вы зря ссылку внимательно не посмотрели, там есть строка:
formData.append("afile", fileInputElement.files[0]); 

Как видите, метод append достаточно умен, чтоб принимать и blob-данные и просто объект File. Я верю, что разработчики оптимизируют процесс чтения и загрузки дял второго случая.
Теперь там, где это возможно (Safari, Chrome, FF 4+) используется FormData, что позволяет избежать загрузки файлов в память (само собой, если есть цель сэкономить память, не стоит их открывать файлы, чтоб делать превьюшки).
Если интересно, см. апдейт статьи.
Спасибо!
За статью спасибо.
Приведенный плагин нужно чуть-чуть допилить — FF 3.6 не поддеоживает FileReader, но поддерживает File.getAsBinary()

} else if($.support.fileReading && xhr.sendAsBinary) {

заменить на

} else if(($.support.fileReading || item.file.getAsBinary) && xhr.sendAsBinary) { (извините, нечаянно нажал отправить)
Опять соврал — тестировал под FF 3.5. Также еще одно замечание по поводу имени файла — вместо строки
var filename = item.replaceName || item.file.name; 

нужно
 var filename = item.replaceName || item.file.name || item.file.fileName;

xhr.setRequestHeader(«Content-Type», «multipart/form-data, boundary=»+boundary);
Запятую нужно заменить на ";"
self.xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary='+boundary);

У меня node.js(formidable) не захотела обрабатывать такую форму.
Да и ещё… если есть возможность получить правельный mime-type для файла, то почему бы его не выставить?
body += 'Content-Type: ' + params.file.type + '\r\n\r\n';
Спасибо за статью, ваши наработки оказались очень полезными.
На данный момент могу сказать что всё работает отлично в Mozilla Firefox 26, Chromium 27. В Opera 12.16 при Drag&Drop при перетаскивании нескольких файлов — залетает только 1, через мультивыбор из поля input:file — нормально, как и должно. А для всяких недобраузеров (кроме Opera), — просто делаются по старинке несколько input:file полей с возможностью добавить ещё, пользователи IE сами выбрали страдания, пусть наслаждаются.
Ну я для недобраузеров делаю еще проще: просто одно поле input:file оставляю :)

Еще стоит отметить, что все работает отлично также в ИЕ 10+
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.