Как известно AIR мобильные приложения желательно делать размерами до 20 Мб в запакованном виде. Это не всегда удается и поэтому единственное решение - загружать контент юзером после установки приложения.
Я планирую использовать URLStream, но мне кажется, что того же можно достичь как минимум ещё 3 способами: URLLoader напрямую обращаясь к файлам, URLLoader который дергает PHP-скрипт, который возвращает контент и FileReference. Скажите какой способ лучше использовать?
Вторая проблема - это необходимость все делать асинхронно, что приводит к увеличении времени загрузки, особенно если файлов десятки и сотни. Вот пример моего приложения, которое проверяет есть ли у юзера нужные файлы и, если нет, то загружает их. Все работает, но мне не нравится (по сути *****код).
Есть возможность поставлять контент в виде zip-архивов, но почему-то не доверяю этому методу из-за возможных ошибок при распаковке. Можно ли как-то проверять контрольную сумму файлов? Не нашел как это можно сделать.

Код AS3:
[Bindable] public var backEffectsAC:ArrayCollection = new ArrayCollection([
{name:'Pad track', tip: 'loop', periodicity:2, used: true, path:['sounds/11.mp3','sounds/12.mp3','sounds/13.mp3'], level:8},
{name:'Key track', tip: 'loop', periodicity:2, used: true, path:['sounds/21.mp3','sounds/22.mp3','sounds/23.mp3'], level:8},
{name:'Atmosphere', tip: 'loop', periodicity:2, used: true, path:['sounds/31.mp3','sounds/32.mp3','sounds/33.mp3'], level:8},
{name:'Priboi', tip: 'fx', periodicity:2, used: true, path:'sounds/4.mp3', level:8},
{name:'Sitar', tip: 'fx', periodicity:2, used: true, path:'sounds/5.mp3', level:8},
{name:'Gitar', tip: 'fx', periodicity:2, used: true, path:'sounds/6.mp3', level:8},
{name:'Percusion', tip: 'fx', periodicity:2, used: true, path:'sounds/7.mp3', level:8},
{name:'Priboi', tip: 'fx', periodicity:2, used: true, path:'sounds/8.mp3', level:8},
{name:'Sitar', tip: 'fx', periodicity:2, used: true, path:'sounds/9.mp3', level:8},
{name:'Gitar', tip: 'fx', periodicity:2, used: true, path:'sounds/10.mp3', level:8}
]);
private var fileCount:int;
private var arrayCount:int;
private var fileName:String;
private var urlStream:URLStream;
private var fileStream:FileStream;
private var fileData:ByteArray = new ByteArray();
protected function creationCompleteHandler(event:FlexEvent):void
{
var file:File = File.applicationStorageDirectory.resolvePath("sounds/7.mp3");
if (!file.exists) {
alert.open(this, false);
updateAlertPosition(alert);
fileCount = 0;
arrayCount = 0;
downloadNow();
}
}
private function downloadNow():void
{
if (backEffectsAC.getItemAt(fileCount).path is Array){
var tempArray:Array =backEffectsAC.getItemAt(fileCount).path;
if (arrayCount > tempArray.length-1){
arrayCount = 0;
fileCount ++;
if (fileCount >= backEffectsAC.length){
alert.close();
alertShow("Files downloaded succesfully") ;
return;
}
downloadNow();
} else {
fileCount --;
fileName = String(tempArray[arrayCount]).slice(7);
arrayCount++;
getFileRemote(fileName);
}
} else {
fileName = String(backEffectsAC.getItemAt(fileCount).path).slice(7);
getFileRemote(fileName);
}
}
private function updateAlertPosition(wnd:SkinnablePopUpContainer):void
{
if (wnd.isOpen) {
wnd.width = systemManager.screen.width;
wnd.height = systemManager.screen.height;
}
}
public function getFileRemote(pathRemote:String):void
{
urlStream = new URLStream();
var urlReq:URLRequest = new URLRequest('http://mysite.com/downloads/sounds/' + pathRemote);
urlStream.addEventListener(Event.COMPLETE, loaded);
urlStream.load (urlReq);
}
private function loaded(event:Event):void
{
urlStream.removeEventListener(Event.COMPLETE, loaded);
urlStream.readBytes (fileData, 0, urlStream.bytesAvailable);
resolveFile(fileName);
}
private function resolveFile(pathLocal:String):void
{
var fileLocal:File = File.applicationStorageDirectory.resolvePath('sounds/' + pathLocal);
fileStream = new FileStream();
fileStream.addEventListener(Event.CLOSE, fileClosed);
fileStream.openAsync(fileLocal, FileMode.WRITE);
fileStream.writeBytes(fileData, 0, fileData.length);
fileStream.close();
}
private function fileClosed(event:Event):void
{
fileStream.removeEventListener(Event.CLOSE, fileClosed);
fileCount ++;
if (fileCount >= backEffectsAC.length){
alert.close();
alertShow("Files downloaded succesfully") ;
return;
}
downloadNow();
}
protected function alert1_ok_clickHandler(event:MouseEvent):void
{
alert1.close();
}
protected function alertShow(txt:String):void
{
alert1.open(this, false);
labelTxt.text = txt;
updateAlertPosition(alert1);
}
]]>
</fx:Script>
<fx:Declarations>
<s:SkinnablePopUpContainer id="alert" backgroundAlpha="0.5" backgroundColor="0x000000">
<s:Panel title="Loading" horizontalCenter="0" verticalCenter="0" width="50%" height="50%">
<s:VGroup gap="10" verticalAlign="middle" horizontalAlign="center" width="100%" height="100%">
<s:BusyIndicator />
<s:Label width="100%" height="100%" fontSize ="24" textAlign="center" text="Wait please..."/>
</s:VGroup>
</s:Panel>
</s:SkinnablePopUpContainer>
<s:SkinnablePopUpContainer id="alert1" backgroundAlpha="0.5" backgroundColor="0x000000">
<s:Panel title="Error" horizontalCenter="0" verticalCenter="0" width="50%" height="50%">
<s:VGroup gap="10" verticalAlign="middle" horizontalAlign="center" width="100%" height="100%">
<s:Label id="labelTxt" width="100%" height="100%" fontSize ="24" textAlign="center"/>
<s:HGroup width="100%" bottom="0" horizontalAlign="center">
<s:Button label="OK" click="alert1_ok_clickHandler(event)" width="50%" styleName="dialogDefaultButton"/>
</s:HGroup>
</s:VGroup>
</s:Panel>
</s:SkinnablePopUpContainer>
</fx:Declarations>