Добрый день! Недавно для одного веб-приложения на Flex'e потребовалось сделать drag'n'drop загрузку фотографий. Flash не позволяет напрямую это реализовать, хотя в приложениях AIR такая фунциональность присутствует. Для решения задачи потребовалось применить HTML5 File API.
Таким образом решение задачи разбивается на несколько этапов. Первый этап — обработка drag'n'drop файлов с помощью File API. Все загруженные файлы добавляются в список из которого потом будут передаваться во Flash.
Второй этап — передача загруженного файла во Flash. Для связи с внешним миром во Flash есть класс ExternalInterface, он позволяет вызывать JS функции, а так же делать доступными для вызова Flash функции. К сожалению вызов функций из Flash у меня стабильно не заработал, поэтому пришлось запускать внутри таймер и вызывать JS функцию для передачи уже загруженных файлов.
Основной проблемой стала форма передачи фотографии. Внутри Flash'a мне было удобно работать с фотографией как с объектом класса ByteArray. Так как в JS нет аналога класса ByteArray, то пришлось при передачи использовать дополнительное преобразование данных в base64. В File API есть соответсвующая функция — readAsDataURL(), которая возвращает файл в виде строки в кодировке base64. Полученную строку мы передаем во flash и с помощью Base64Decoder получаем объект класса ByteArray, содержащий нашу исходную фотографию.
В итоге мы получаем drag'n'drop загрузку фотографий во Flex приложение. Небольшой кусочек кода для вывода фотографий из ByteArray.
У данного решения есть один существенный минус, при загрузке фотографий большого размера, браузер ощутимо нагружает процессор. Возможно есть более оптимальный способ, но я его не нашел.
p.s. Ссылка на работающий пример, не сочтите за рекламу, проект местный и рассчитанный на другую аудиторию. Пример
Таким образом решение задачи разбивается на несколько этапов. Первый этап — обработка drag'n'drop файлов с помощью File API. Все загруженные файлы добавляются в список из которого потом будут передаваться во Flash.
$(document).ready(function() {
var dropZone = $('div#dropZone');
// Проверка поддержки браузером
if (typeof(window.FileReader) == 'undefined') {
dropZone.text('Не поддерживается браузером!');
dropZone.addClass('error');
}
// Обрабатываем событие Drop
dropZone[0].ondrop = function(event)
{
event.preventDefault();
dropZone.removeClass('hover');
dropZone.addClass('drop');
displayFiles(event.dataTransfer.files);
};
});
//--------------------------------------------------------------------------------------------------
var fileList = new Array();
function displayFiles(files)
{
$.each(files, function(i, file)
{
var imgList = $('ul#img-list');
var maxFileSize = 20*1000000; // максимальный размер фалйа - 1 мб.
if (!file.type.match(/image.*/))
{
// Отсеиваем не картинки
return true;
}
// Создаем элемент li и помещаем в него название, миниатюру
// а также создаем ему свойство file, куда помещаем объект File (при загрузке понадобится)
var li = $('<li/>').appendTo(imgList);
$('<div/>').text(file.name).appendTo(li);
// Проверяем размер файла
if (file.size > maxFileSize) {
$('<div/>').text('Файл слишком большой!').appendTo(li);
}
else
{
var img = $('<img/>').appendTo(li);
li.file = file;
// Создаем объект FileReader и по завершении чтения файла, отображаем миниатюру и обновляем
// инфу обо всех файлах
var reader = new FileReader();
reader.onload = (function(aImg, file) {
return function(e) {
aImg.attr('src', e.target.result);
aImg.attr('width', 75);
file.f_data = e.target.result;
//добавляем файл в список файлов на передачу
fileList.push(file);
};
})(img, file);
reader.readAsDataURL(file);
}
});
}
//--------------------------------------------------------------------------------------------------
HTML код для вставки flex приложения, а так же div, в который мы и будем скидывать файлы.
<div style="float:left">
</div>
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="https://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=10,0,0,0" id="web-client" width="800px" align="middle" height="600px">
<param name="allowScriptAccess" value="sameDomain">
<param name="allowFullScreen" value="false">
<param name="movie" value="dd_test.swf"><param name="quality" value="high"><param name="bgcolor" value="#cccccc"><embed src="test.swf" quality="high" bgcolor="#cccccc" name="web-client" allowscriptaccess="sameDomain" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="https://www.adobe.com/go/getflashplayer" width="800" align="middle" height="600">
</object>
<div id="dropzone" style="float:left">
<p>Для загрузки, перетащите файл сюда.</p>
<ul id="img-list"></ul>
</div>
Второй этап — передача загруженного файла во Flash. Для связи с внешним миром во Flash есть класс ExternalInterface, он позволяет вызывать JS функции, а так же делать доступными для вызова Flash функции. К сожалению вызов функций из Flash у меня стабильно не заработал, поэтому пришлось запускать внутри таймер и вызывать JS функцию для передачи уже загруженных файлов.
Основной проблемой стала форма передачи фотографии. Внутри Flash'a мне было удобно работать с фотографией как с объектом класса ByteArray. Так как в JS нет аналога класса ByteArray, то пришлось при передачи использовать дополнительное преобразование данных в base64. В File API есть соответсвующая функция — readAsDataURL(), которая возвращает файл в виде строки в кодировке base64. Полученную строку мы передаем во flash и с помощью Base64Decoder получаем объект класса ByteArray, содержащий нашу исходную фотографию.
//код Flex приложения, ответсвенный за приём файла
var dragTimer:Timer;
var file_count_already_have:int;
//Вызвать из флеш ф-ции из JS не удалось, будем опрашивть JS ф-ции по таймеру
public function init_draginteface():void
{
dragTimer = new Timer(3*1000,0); //раз в 3 секунды
dragTimer.addEventListener(TimerEvent.TIMER, dragTimerEvent);
dragTimer.start();
file_count_already_have = 0;
}
//---------------------------------------------------------------------------
public function dragTimerEvent(e:TimerEvent):void
{
dragTimer.stop();
if(ExternalInterface.available)
{
var file_count:int = ExternalInterface.call("dropFileCount"); //Вызов JS ф-ции, которая возращает количество загруженных файлов
if (file_count>0)
{
for(var i:int = file_count_already_have ; i<file_count; i++)
{
dragImageFromJS(i);
}
file_count_already_have = file_count;
}
dragTimer.start();
}
}
//---------------------------------------------------------------------------
public function dragImageFromJS(i:int):void
{
var file_size:int = ExternalInterface.call("getFileSize", i); // Получаем размер файл
var file_name:String = ExternalInterface.call("getFileName", i); // Его имя
var file_ba:ByteArray = new ByteArray;
var base64dec:Base64Decoder = new Base64Decoder();
var start_load:uint = getTimer();
base64dec.decode(ExternalInterface.call("getFile", i));
//и собственно сам файл перекодированный в строку base64
var load_time:uint = getTimer() - start_load;
file_ba = base64dec.toByteArray();
if(file_ba != null)
{
trace("");
trace("add new item [file_name: "+file_name+"] [file_size:"+file_size.toString()+"] [fr_ba_size: "+file_ba.length + "] [load_time: "+load_time.toString()+"]");
loadFromByteArray(file_ba); //отрисовываем изображение внутри Flex приложения
}
}
//JS ф-ции, которые вызываются из Flex приложения
function dropFileCount() //возращает количество файлов
{
return fileList.length;
}
//--------------------------------------------------------------------------------------------------
function getFile(i) //сами файлы в виде строки
{
var data = fileList[i].f_data;
var data_cat = data.substr(23);
return data_cat;
}
//В начале строки содержится информация, которую необходимо отрезать, её длина постоянная
//data:image/jpeg;base64,/9j/4U3ARXhpZgAASUkq
//--------------------------------------------------------------------------------------------------
function getFileSize(i)
{
return fileList[i].size;
}
//--------------------------------------------------------------------------------------------------
function getFileName(i)
{
return fileList[i].name;
}
//--------------------------------------------------------------------------------------------------
В итоге мы получаем drag'n'drop загрузку фотографий во Flex приложение. Небольшой кусочек кода для вывода фотографий из ByteArray.
<mx:Script>
<![CDATA[
//код Flex приложения отвественный за вывод изображения из ByteArray
public function loadFromByteArray(data:ByteArray):void
{
_loader = new Loader();
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE,_load_loader_complete);
_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, errorOnLoad);
_loader.loadBytes(data);
}
public function _load_loader_complete(e:Event):void
{
_loader.removeEventListener(Event.COMPLETE,_load_loader_complete);
var bitmapIm:Bitmap = Bitmap(e.target.content);
this.itemIm.addChild(bitmapIm);
}
]]>
</mx:Script>
<mx:Image source="" id="itemIm" maxWidth="100" maxHeight="100" horizontalAlign="left" verticalAlign="top"/>
У данного решения есть один существенный минус, при загрузке фотографий большого размера, браузер ощутимо нагружает процессор. Возможно есть более оптимальный способ, но я его не нашел.
p.s. Ссылка на работающий пример, не сочтите за рекламу, проект местный и рассчитанный на другую аудиторию. Пример