Pull to refresh

Drag'n'drop файлов во Flex, используя HTML5 File API

Reading time6 min
Views3.1K
Добрый день! Недавно для одного веб-приложения на Flex'e потребовалось сделать drag'n'drop загрузку фотографий. Flash не позволяет напрямую это реализовать, хотя в приложениях AIR такая фунциональность присутствует. Для решения задачи потребовалось применить HTML5 File API.

Таким образом решение задачи разбивается на несколько этапов. Первый этап — обработка 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. Ссылка на работающий пример, не сочтите за рекламу, проект местный и рассчитанный на другую аудиторию. Пример
Tags:
Hubs:
+4
Comments4

Articles