Компания
531,86
рейтинг
21 ноября 2012 в 11:37

Разработка → Загрузка файлов на сервер в 2012 году

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

Я работаю в Почте Mail.Ru, и моей прямой обязанностью является работа с JavaScript во всех его проявлениях. Прикрепление файлов к письму — одна из основных функций любой почты. Мы тут не исключение: у нас уже был Flash-загрузчик, который вполне исправно работал и долгое время нас устраивал. Однако у него был ряд недостатков. Вся верстка, графика, бизнес-логика, и даже локализация были зашиты в нем, в результате чего решение было громоздким, а внести правки мог только Flash-разработчик. В какой-то момент мы поняли, что нам необходим новый механизм. О том, как его создать, пойдет речь в этой статье.

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

  • cookies будут взяты из IE;
  • настройки proxy — та же история;
  • Error #2038, #2048,… — плавающая ошибка, зависит от сочетания сеть + браузер + Flash Player;
  • AdBlock и т.п. — без комментриев.

И вот, глядя на всё это, мы решили: пора, момент настал — и сформировали следующие требования:

  • множественный выбор файлов;
  • получение информации (название, размер и mime-тип);
  • создание предпросмотра изображения до загрузки;
  • масштабирование, кадрирование и поворот на клиенте;
  • загрузка всего, что получилось, на сервер + CORS;
  • независимость от сторонних библиотек;
  • расширяемость.


Последние 4 года всё активнее обсуждаются возможности HTML5, в том числе и File API. По этому поводу написано множество статей, есть работающие примеры. Казалось бы, вот готовый инструмент для решения поставленных задач. Но так ли все просто, как кажется на первый взгляд? Рассмотрим статистку пользователей Mail.Ru по браузерам. Из списка выбраны только те версии, который поддерживают File API, пусть и не на 100%.



Как видно из диаграммы, чуть более 63% используемых браузеров поддерживают File API:

  • Chrome 10+ (~26%)
  • Firefox 3.6+ (~19%)
  • Opera 11.10+ (~17%)
  • Safari 5.4+ (~1.4%)

Также не стоит забывать о мобильных устройствах, доля которых растет день ото дня. iOS 6 уже поддерживает File API.

Internet Explorer обещает поддержку с 10-ой версии.

Но 63% — это отнюдь не 100%. Так что отказываться от Flash’а рано.

Таким образом, задача свелась к созданию механизма, который совмещал бы в себе обе технологии (File API и Flash) и был реализован так, чтобы конечному разработчику было неважно, как происходит загрузка файлов. В ходе работы возникла идея оформить все наработки в виде отдельной библиотеки (унифицированного API), которая бы работала независимо от окружения и могла использоваться не только в рамках Почты Mail.Ru, но и где угодно.

Рассмотрим на конкретных примерах, как проходил процесс разработки.


Получение списка файлов


Вот так выглядит получение списка файлов на HTML5. Всё очень просто.

<input id="file" type="file" multiple />
<script>
     var input = document.getElementById("file");
     input.addEventListener("change", function (){
          var files = input.files;
     }, false);
</script>

Но что делать, когда не поддерживается File API, но зато поддерживается Flash? Основной принцип работы с Flash в том, что всё взаимодействие происходит непосредственно через него. Нельзя просто взять и вызвать диалог выбора файлов.

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

Поэтому Flash-объект размещается над нужным input. Сделано это очень просто: на весь документ вешается обработчик события mouseover, и при наведении на input[type=«file»] в “родитель” публикуется Flash-объект и занимает всё его пространство.

При клике по флешке открывается файловый диалог, пользователь в нем что-то выбирает и кликает ОК. После чего данные передаются от Flash в JS посредством ExternalInterface. JS связывает полученные данные с нужным input и эмулирует событие «change».
Flash --> js
[[Flash]] --> jsFunc([{
     id:  "346515436346",    // уникальный идентификатор
     name:  "hello-world.png",   // название файла
     type:  "image/png",    // mime-type
     size:  43325   // размер
}, {
     // etc.
}])

Все дальнейшее взаимодействие между JS и Flash будет осуществляться через единственный доступный метод у Flash-объекта. Первым аргументом передается название команды, вторым — объект параметров с двумя обязательными полями: id файла и callback. Callback будет вызван из Flash по завершении команды.

flash.cmd("imageTransform", {
	id: "346515436346",    // идентификатор файла
	matrix: { },    // матрица трансформации
	callback: "__UNIQ_NAME__"
});

После совмещения двух способов получилось API, максимально приближенное к Native JS. Единственное различие — это способ получения файлов. Теперь мы используем метод API, т.к. свойство files у input есть только в том случае, когда браузер поддерживает HTML5/File API; в случае с Flash cписок берется из связанных с ним данных.

<span class="js-fileapi-wrapper" style="position: relative">
	<input id="file" type="file" multiple />
</span>
<script>
	var input = document.getElementById("file");
	FileAPI.event.on(input, "change", function (){
		var files = FileAPI.getFiles(input);
	});
</script>

или так
<span class="js-fileapi-wrapper" style="position: relative">
	<input id="file" type="file" multiple />
</span>
<script>
	var input = document.getElementById("file");
	FileAPI.event.on(input, "change", function (evt){
		var files = FileAPI.getFiles(evt);
	});
</script>


Фильтрация


Как правило, при загрузке файлов есть ряд ограничений. Одни из самых популярных — размер файла, тип и ширина/высота изображения. Стандартная схема решения таких задач: сначала загрузить файл на сервер, а после валидации сообщить юзеру, что файл не подошел, «попробуйте ещё раз». Создавая метод фильтрации, я постарался решить эту проблему, дав возможность оперировать во время фильтрации более детальной информацией о файле.

В чем же сложность? Вся соль в том, что изначально после получения списка файлов мы имеем только минимальные сведения, такие как название, размер и тип. Для того, чтобы получить более детальную информацию, файл нужно прочесть. Это можно сделать через FileReader.
IE10 & FileReader
​// Если вы не знали, IE10 поддерживает HTML5/FileAPI:
var reader = new File​Reader;
reader.readAsBinaryString(file); // error: Object doesn't support method or property "readAsBinaryString"

​// Но, выход есть!
var reader = new FileReader;
reader.onload = function (evt){
	var base64 = evt.result.replace(/^data:[^,]+,/, '');
	var binaryString = window.atob(base64); // bingo!
};
reader.readAsDataURL(file);

В итоге получился следующий метод фильтрации:

FileAPI.filterFiles(files, function (file, info){
	if( /^image/.test(file.type) ){
		return info.width > 320 && info.height > 240;
	} else if( file.size ){
		return file.size < 10 * FileAPI.MB;
	} else {
		// Увы, нет поддержки File API и Flash, валидировать придется на сервере.
		// Случай очень редкий, но в рамках большого проекта, приходиться учитывать и его.
		return true;
    }
}, function (files, ignore){
	if( files.length > 0 ){
		// ...
	}
});


Также “из коробки” поддерживается определение высоты и ширины изображения, а ещё есть возможность самому реализовать сбор нужной информации:

FileAPI.addInfoReader(/^audio/, function (file, callback){
	// собираем нужную информацию
	// и возвращаем её
	callback(
		false,    // или текст ошибки
		{ artist: "...", album: "...", title: "...", ... }
	);
});



Работа с изображениями


В процессе создания API хотелось получить инструмент по работе с изображением — например, с созданием предпросмотра — и чтобы базовая функциональность поддерживалась HTML5 и Flash.


Flash
Первым делом нужно было понять, как это сделать через Flash, т.е. что передать в JS, чтобы построить изображение. Как вы поняли, это осуществляется при помощи Data URI. Flash читает файл как Base64, передает в JS. В начало добавляем «data:image/png;base64,» и используем полученную строку в качестве «src».

Happy end? Увы, но в IE6-7 нет поддержки Data URI, а IE8+, поддерживающий Data URI, не обрабатывает больше 32 КБ. В этих случаях JS публикует вторую флешку, которой передает Base64, а она восстанавливает изображение.


HTML5
Тут вам нужно сначала получить оригинал, а потом через Сanvas провести необходимые трансформации. Оригинал можно получить двумя способами. Первый — прочесть файл как DataURL при помощи FileReader. Второй — URL.createObjectURL создает ссылку на файл, связанную с текущим табом. Конечно для создания предпросмотра достаточно второго способа, но не все бразуеры его поддерживают. А некоторые не поддерживают сопутствующий URL.revokeObjectURL, который сообщает браузеру, что больше не нужно держать ссылку на файл.

После совмещения всех этих способов получился класс FileAPI.Image:

  • crop(x, y, width, height) — кадрирование;
  • resize(width[, height]) — масштабирование;
  • rotate(deg) — поворот;
  • preview(width, height) — кадрирует и масштабирует;
  • get(callback) — получить итоговое изображение.


Все эти методы заполняют матрицу трансформации, и только при вызове метода get, она будет применена. Трансформация происходит через Canvas или внутри Flash, когда работает через него.

Описание матрицы
{   // параметры фрагмента оригинала
	sx:  Number,
	sy:  Number,
	sw:  Number,
	sh:  Number,

    // требуемые размеры
	dw:  Number,
	dh:  Number,
	deg: Number
}

Пример использования
FileAPI.Image(imageFle)
	.crop(300, 300)
	.resize(100, 100)
	.get(function (err, img){
		if( !err ){
			images.appendChild(img);
		}
	})
;


Процесс ресайза


В нашу жизнь уже давно вошли зеркалки и просто «мыльницы», которые при цене в 1.2К рублей выдают от 10Mpx. И когда мы начали сжимать такие картинки, то получили примерно это:



Как видите, ничего хорошего — сплошные искажения. Но, если сжимать в два раза, потом ещё и ещё, пока не получим требуемый размер, то результат заметно лучше.



Вот, сравните, разница налицо:



Если добавить немного шарпа, будет просто идеально.

Еще мы пробовали другие способы, такие как бикубическая интерполяция и алгоритм Ланцоша. Они дают чуть лучший результат, но очень медленные: 1.5s против 200-300ms. Также данный метод дает одинаковый результат в Canvas и Flash.


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


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


iframe
Да, и спустя годы он всё ещё в строю:
<form
	target="__UNIQ__"
	action="/upload"
	method="post"
	enctype="multipart/form-data">
	<iframe name="__UNIQ__"></iframe>
	<input name="files" type="file" />
	<input name="foo" value="bar" type="hidden" />
</form>

Сначала создаётся форма-транспорт с iframe внутри (атрибут target формы и name iframe должны совпадать). Потом в неё нужно переместить input[type=«file»], т.к. если поместить клон, он будет «пустым». Именно поэтому на события подписываемся через методы API, чтобы сохранить их при клонировании. После чего вызываем form.submit() и всё содержимое формы отправляется через iframe. Ответ получаем при помощи JSONP.


Flash/Silverlight
Сначала появился Flash, за ним Silverlight, который собирался стать убийцей Flash, но что-то не срослось. В общем, там всё просто: JS вызывает метод у Flash-объекта и передаёт id файла, который нужно загрузить, а Flash, в свою очередь, все состояния и события дублирует в JS.


XMLHttpRequest + FormData
Теперь можно отправить не просто текстовые данные, но и бинарные. Делается это очень просто:
// собираем данные для отправки
var form = new FormData
form.append("foo", "bar"); // первый параметр название POST-параметра,
form.append("attach", file); // второй строка, файл или blob

// отправляем на сервер
var  xhr = new XMLHttpRequest;
xhr.open("POST", "/upload", true);
xhr.send(form)

Но что делать, когда вам нужно отправить не файл, а, например, Canvas? Тут есть два пути. Первый, он же самый правильный и простой, это, конечно же, преобразовать Canvas в Blob:
canvasToBlob(canvas, function (blob){
	var form = new FormData
	form.append("foo", "bar");
	form.append("attach", blob, "filename.png"); // не все поддерживают третий параметр

	// ...
});

Как вы поняли, не везде есть возможность провернуть такой фокус. Если у Canvas отсутствует метод Canvas.toBlob (либо его нельзя реализовать), идем другим путем. Он также подходит для тех браузеров, которые не поддерживают FormData.

Суть этого способа в том, чтобы руками составить multipart-запрос и отправить его на сервер. Для Canvas код будет выглядеть следующим образом:

var dataURL = canvas.toDataURL("image/png"); // или результат чтения FileReader
var base64 = dataURL.replace(/^data:[^,]+,/, ""); // отрезаем начало
var binaryString = window.atob(base64); // разворачиваем Base64

// а теперь собираем muptipart, ничего сложного
var uniq = '1234567890';
var data = [
	  '--_'+ uniq
	, 'Content-Disposition: form-data; name="my-file"; filename="hello-world.png"'
	, 'Content-Type: image/png'
	, ''
	, binaryString
	, '--_'+ uniq +'--'
].join('\r\n');

var xhr = new XMLHttpRequest;
xhr.open('POST', '/upload', true);
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=_'+uniq);
xhr.sendAsBinary(data);

Если браузер не поддерживает xhr.sendAsBinary
if( xhr.sendAsBinary ){
	// ...
}
else {
	var bytes = Array.prototype.map.call(data, function(c){ return c.charCodeAt(0) & 0xff; });
	xhr.send(new Uint8Array(bytes).buffer);
}


В итоге родился метод:
var xhr = ​FileAPI.upload({
   url:  '/upload',
   data:  { foo: 'bar' },
   headers:  { 'Session-Id': '...' },
   files:   { images: imageFiles, others: otherFiles },
   imageTransform:  { maxWidth: 1024, maxHeight: 768 },
   upload: function (xhr){},
   progress: function (event, file){},
   complete: function (err, xhr, file){},
   fileupload: function (file, xhr){},
   fileprogress: function (event, file){},
   filecomplete: function (err, xhr, file){}
});​


У него куча параметров, но особое внимание я уделю imageTransform. Через него задается информация для трансформации изображений на клиенте. Работает как через Flash, так и через HTML5. Но это ещё не всё: также imageTransform может быть множественным:
{
	 huge:   { maxWidth: 800, maxHeight: 600, rotate: 90 },
	 medium: { width: 320, height: 240, preview: true },
	 small:  { width: 100, height: 120, preview: true }
}

Т.е. помимо оригинала на сервер уйдет ещё и три его копии. Зачем? Моё мнение таково: если вы можете перенести нагрузку с сервера на клиент — сделайте это. На сервере должна остаться только минимальная валидация входящих данных.

Также функция upload возвращает xhr-образный объект, т.е. он реализует некоторые свойства и методы XMLHttpRequest, такие как:

  • status — HTTP status code
  • statusText — HTTP status text
  • responseText — ответ сервера
  • getResponseHeader(name) — получить заголовок ответа сервера
  • getAllResponseHeaders() — получить все заголовки
  • abort() — отменить загрузку

Хотя HTML5 и умеет загружать файлы одним запросом, стандартный механизм Flash позволяет грузить только по одному. Кроме того, грузить все сразу не очень удачная идея — пользователь может и передумать.

Так вот, тот xhr, который возвратил upload, на самом деле является proxyXHR. Его методы и свойства отражают состояния именно для того файла, который грузится в данный момент. Если пользователь решил отменить загрузку, то действие будет выполнено для файла, который загружается в данный момент.



Эпилог


В завершение, я хочу показать вам маленький пример загрузки файлов при помощи drag'n'drop:
<div id="el" class="dropzone"></div>
<script>
	if( FileAPI.support.dnd ){
		// элемент, куда можно кинуть файлы
		var el = document.getElementById("el");

		// подписываемся на события связанные Drag'n'Drop
		FileAPI.event.dnd(el, function (over){
			// этот метод, будет срабатывать при enter/leave на элемент
			if( over ){
				el.classList.add("dropzone_hover");
			} else {
				el.classList.remove("dropzone_hover");
			}
		}, function (dropFiles){
			// Пользователь бросил файлы
			FileAPI.upload({
				url: "/upload",
				files: { attaches: dropFiles },
				complete: function (err, xhr){
					if( !err ){
						// файлы загружены
					}
				}
			});
		});
	}
</script>


Библиотека лежит на github, багрепорты и pull requests приветствуются.

Полезные ссылки
https://github.com/mailru/FileAPI (demo)
Mail.ru github (Tarantool, fest и многое другое)
input[type=«file» multiple]
File API support
FileReader
URL.createObjectURL, URL.revokeObjectURL
XMLHttpRequest
FormData

Автор: @RubaXa

Комментарии (71)

  • 0
    Тема уже изъезжана в доль и поперек, но, на мой взгляд, пост лишим не будет.
    • –3
      Положу здесь для связности эту ссылку: habrahabr.ru/company/yandex/blog/154151/
      • +2
        Предлагаю и у вас для связности добавить нас.
        • 0
          так ты и добавь ;)
          • +2
            Да понятно, надеялся Роман сразу сделал это зеркально.
  • +4
    Это все конечно интересно, но про ресайз картинок вы немного загнули.
    Чем вы ресайзите — nearest neighbour что-ли?
    Совершенно не убедили в пользе многоходовой комбинации
    • 0
      Не совсем, во Flash bilinear interpolation + пошаговое сжатие. Польза видна именно при создании предпросмотра для огромных изображений. Если этого не делать, то некоторые изображения выглядят как рандомный набор пикселей.
    • +1
      Вот более показательный пример 5500х4087 -> 50x50 www.rubaxa.org/screenshot/4ab76ad2892ec56586eb9d97f0c9.png
      • НЛО прилетело и опубликовало эту надпись здесь
        • +1
          Поробуйте, это «дорого».
      • +1
        Интерполяция это не совсем ресайз. Разве нету функции специально для ресайза? (я лично не в курсе, на флеше не писал никогда)

        Похоже у вас просто алиасинг проступает. Вот если бы вы сильно заблурили картинку перед интерполяцией — тогда бы одношаговая интерполяция приводила бы к нормальному результату.

        PS.
        Когда то давно решал похожую задачу с аплоадером на ява-апплетах :-)
        • 0
          Во Flash есть, но путем методов проб и замеров поняли, что этот способ самый быстрый и дает приемлимый результат.
      • 0
        Первая картинка такая ужасная из-за того, что флеш не умеет нормально ресайзить? Может тогда делать это на стороне сервера?
        • 0
          Это пример на canvas, но через flash будет тоже самое, если сжимать разом. Сжимать можно и на сервере, но это не отменяет того, что предпросмотр будет «кривой».
    • +2
      С уменьшением всегда были проблемы.
      Это на увеличение разработано очень много алгоритмов, потому что текстуры раньше были маленькие.
      Вообще никакой интерполяцией нельзя получить нормальное уменьшение — тут надо работать на контурах и контрастах.
      Хороший пример — сравним обычный tri-linear и AA.

      • 0
        Да, красиво. Но увы на клиенте этого нету (native) :[
      • 0
        сравним обычный tri-linear и AA

        Не AA, а AF (anisotropic filtering)
  • 0
    Исходники библиотеки в несжатом виде, не помешали бы, для удобства просмотра)
  • +4
    Исходов Флеш-части нету чтоль?
    Лицензия какая?
  • –1
    А не подскажите процент браузеров без поддержки Flash?
    • 0
      И ещё интересен процент без поддержки и FileAPI и Flash :)
      • 0
        Без поддержки Flash, это ещё и AdBlock, на наших проектах ~5%
  • +1
    Скажите пожалуйста, на каких условиях ваши скрипты можно использовать в своих проектах?
    • +1
      BSD
      • +2
        Вы мой вопрос про исходники Флеш-части принципиально игнорируете?
        • +5
          Сорри, не специально. В ближайшие время обязательно добавлю :]
          • 0
            Ок, ждём. :)
  • +1
    Спасибо большое! Для меня эта тема была актуальна. Я использовал флеш для написания контрола, если флеш не поддерживался показывался стандартный контрол, но вот до такого не додумался. Отлично все работает, особенно превью :)
    • 0
      На самом деле это была основная задача и головная боль, как работать с Flash, чтобы не знать о нём :]
      • 0
        Спасибо за статью! :)
  • 0
    А как же про поддержку догрузки и защиту от разрывов связи (отправка частями)?
    • 0
      Изначальной задачей было сохранить текущую функциональность и избавиться от недостатков. Функциональность будет наращиваться и загрузка по частям первая в списке.
    • 0
      Планируется, но там не все однозначно, так как нужна поддержка сервера.
  • +1
    Обновите пожалуйста файл лицензии на Git Hub, по-моему там просто шаблонный текст. Очень хотелось бы использовать в своём проекте, если лицензия подойдет :)
    • +1
      Упс, выше увидел, что BSD. Спасибо :)
  • +3
    Вот за что люблю Хабр, что он подкидывает статьи именно в тот момент, когда они нужны. Спасибо RubaXa.
  • 0
    Лично я частенько уменьшаю, даже в графическом редакторе, в 2 шага: до удвоенного необходимого, потом до нужного.
  • +1
    Автоматически поворачивать картинку на основе EXIF информации есть в планах? У 90% простых людей на этой стадии возникают проблемы.
    • 0
      Конечно, даже сейчас это можно делать через imageTransform на основе FileAPI.getInfo, но дополнительный параметр при upload обязательно будет.
  • +1
    Добавьте проверку на Safari, он «особенный», говорит, что multiple file input поддерживает, а передает что-то неправильно, в результате на сервер приходят файлы с filesize = 0, короче их нет.
    Небольшое обсуждение можно найти здесь.
    Я для Safari multiple upload на флэше реализую из-за этого
    • 0
      это для Safari на винде, на маке не в курсе
      • 0
        Хм, не знал, но это не важно, для Safari < 5.4 и так Flash. Вся библиотека построена на концепции поддержки нужной технологии в необходимом объеме.

        P.S. Проверил в 5.1.7 (windows), размер файла есть.
        • 0
          то есть Вы с флэшем проверили?
          • 0
            Нет, вот это — jsfiddle.net/rHd26/6/
            • 0
              хм, забавно
              Windows 7 + Safari 5.1.7 там же дает
              fileName:calendar_current_date_highlight.jpg name:calendar_current_date_highlight.jpg fileSize:0 lastModifiedDate:Thu Jan 01 1970 04:00:00 GMT+0400 (Ìîñêîâñêîå âðåìÿ (çèìà)) size:0 type:image/jpeg fileName:calendar_in_gmail.png name:calendar_in_gmail.png fileSize:0 lastModifiedDate:Thu Jan 01 1970 04:00:00 GMT+0400 (Ìîñêîâñêîå âðåìÿ (çèìà)) size:0 type:image/png
            • 0
              На mailru.github.com/FileAPI тоже по нулям при нескольких файлах.
              Там смысл именно в том, что когда выбираешь 1 файл с аттрибутом multiple, то все работает.
              Баг появляется только если несколько файлов выбрать.
              • 0
                Да, придется добавить проверку, но так всё хорошо было, эх…
                • +1
                  и не говорите ))
                  Еще очень рекомендую проверить в режиме флэша, когда сам флэш передает на сервер (этот режим используется в библиотеке?), по HTTPS (только нужен правильный SSL сертификат)
                  • 0
                    У нас почта работает только по https, этот режим должен быть оттестирован хорошо.
                  • 0
                    Можно подробнее? Мы тоже мучились с https. Пока решили её ping'ом crossdomain.xml на том домене, куда грузим файл.
                    • 0
                      я не флэш-программер, поэтому не в курсе, как там что внутри, опишу, как я это понимаю.
                      В режиме передачи файла по HTTPS флэшем происходит проверка сертификата, то есть флэш «выступает в роли браузера» (так как в нормальной ситуации этим занимается браузер).
                      У некоторых браузеров была проблема в том, что база сертификатов корявая/ограниченная, то есть они доверяют только проверенным источникам (к примеру, GoDaddy), поэтому если у вас:
                      1) self-signed сертификат (что вполне вероятно на dev-стадии)
                      2) нормальный сертификат, но не содержит в себе инфу от подписавшего его источника
                      то появляются проблемы с тем, что сертификат не принимается браузером.
                      Флэш ведет себя точно также, то есть тупо отбрасывает такие сертификаты и выдает ошибку IO #2038.
                      В первом случае тут ничего не поделаешь.
                      Во втором нужно просто добавить корневой сертификат проверенного источника (который подписал ваш сертификат) внутрь вашего сертификата (это для Nginx) — то есть добавить цепочку сертификатов.
                      В случае апача добавить параметров SSLCACertificateFile и SSLCertificateChainFile.

                      По nginx смотреть тут.
                      • 0
                        У нас так и сделано — это не панацея. Ошибка плавает в зависимости от сочетания: сеть [+ proxy] + браузер + flash player. Именно поэтому, перед тем как переключиться на Flash, пробуем забрать crossdomain.xml, в случае ошибки переходим на iframe.
                        • 0
                          ну тут все только по собственному опыту, методом тыков и «затыков», я пришел именно к тому, что проблема в недостаточной инфе в сертификате, после рекомендаций со ссылки по nginx, проблема полностью пропала. Насчет проблем сети не знаю, не испытывал, но логично, что выпадет тоже IO error, что логично
                        • 0
                          1) кстати, если, к примеру, вы уже пропинговали и решили, что можно заюзать флэш, пока пользователь выбирал файл для загрузки, у него отвалилась сеть, он в конце концов выбрал свой файл и флэш пытается отправить его, что произойдет?
                          2) если решили юзать флэш аналогично первому случаю, пользователь выбрал большой файл и посреди передачи флэшем отвалилась сеть, что будет?
                    • 0
                      По крайней мере, такая логика в uploadify флэше была в версии 2.10 (вроде), они там использовали флэш из swfupload.
                      Я недавно как раз обертку писал с учетом поддержки HTML5 некоторыми браузерами и присутствием/отсутствием флэша в системе.
                      Так что все объяснение только по опыту работы с этим флэшем
          • 0
            Очень изменяюсь, ступил, выбрал только один файл, там и в правду 0. Единственное, что радует, Safari больше не выпускается под Windows.
  • 0
    Извините, что комментарий не совсем в тему. В почте mail.ru, когда нажимаешь «Ответить» на письмо, который ТЫ сам отправил кому-то, в поле «получатель» подставляется ТВОЙ ЖЕ адрес. Как вы считаете, это нормальная функциональность? Мое мнение, я думаю, понятно :)
    • 0
      Вы правы, нелогично. Я, честно говоря, почти всегда жму «Ответить всем» (и тогда, логично, все подставляются).

      Исправим.

      Правда, остается вопрос — если я нажал «Ответить» на письмо, в котором несколько получателей — кому из них пойдет ответ? Наверное, самое логичное — всем. Правда, тогда получается что в Отправленных кнопка «Ответить» работает так же, как «Ответить всем».
      • 0
        Сделать формочку выбора, кому отвечать, и кнопку ответить всем
        • 0
          Какую еще «формочку выбора»? :) В форме написания письма и так есть поле «кому отвечать». И кнопка «ответить всем», конечно, тоже есть.
  • 0
    Позвольте совсем идиотский вопрос. А нельзя было совсем без флеша? Через multipart всегда можно загрузить. Неужели так нужен ресайз на клиенте, чтобы поддерживать флеш-компонент?
    • 0
      Думаю, для таких больших порталов, как mail.ru, каждый такт важен.
    • 0
      Конечно можно, Flash работает только в том случае, если нет поддержки File API в нужно объеме. Всё зависит от доли IE < 10, да и размеров проекта.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка