Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AIR AS3 file download is corrupted when bandwidth is low

I am using a Downloader class to get large files from a IIS server on WS2012 and handle download progress.

It works fine, however when the client's bandwidth is too saturated, Progress events aren't fired anymore and after a certain amount of time the download just stops (the Complete event seems to be triggered?), despite the download being unfinished, leaving the client with a corrupt file.

I couldn't find out how to solve this, or even what strategy I should take on this problem (finish the download and display an error? Wait for bandwidth availability to get my next piece of bytes?)

Here's the Downloader.as class

public class Downloader extends EventDispatcher
{
    [Event(name="DownloadComplete", type="DownloadEvent")]

    public static var spd:int = 0;
    private var file:File;
    private var fileStream:FileStream;
    private var url:String;
    private var urlStream:URLStream;

    var mc_background:MovieClip;
    var howManyTimes:Number = 3; //How many times per second  the download speed will be traced
    var bytesLoaded:Number = 0; //don't change, necessary for calculation
    var lastTime:int = 0; //don't change, necessary for calculation


    private var waitingForDataToWrite:Boolean = false;

    public function Downloader(s:MovieClip)
    {
        mc_background = s;

        lastTime = getTimer();
        urlStream = new URLStream();

        urlStream.addEventListener(Event.OPEN, onOpenEvent);
        urlStream.addEventListener(ProgressEvent.PROGRESS, onProgressEvent); 
        urlStream.addEventListener(Event.COMPLETE, onCompleteEvent);

        fileStream = new FileStream();
        fileStream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, writeProgressHandler)

    }

    public function download(formUrl:String, toFile:File):void {
        this.url = formUrl;
        this.file = toFile;
        mc_background.pb.file_txt.text = file.name;
        fileStream.openAsync(file, FileMode.WRITE);
        urlStream.load(new URLRequest(url));
    }

    private function onOpenEvent(event:Event):void {
        waitingForDataToWrite = true;

        dispatchEvent(event.clone());
    }

    private function onProgressEvent(event:ProgressEvent):void {
        var time:int = getTimer();
        if(time - lastTime >= (1000/howManyTimes))
        {
            var kiloBytes:Number = (event.bytesLoaded - bytesLoaded)/1000;
            var timeInSecs:Number = (time - lastTime)/1000;

            var kbsPerSecVal:Number = Math.floor(kiloBytes/timeInSecs);
            trace(kbsPerSecVal + " kbs/s");

            mc_background.pb.speed_txt.text = kbsPerSecVal + " kbs/s";
            bytesLoaded = event.bytesLoaded;
            lastTime = getTimer();
        }
        if(waitingForDataToWrite){
            writeToDisk();
            dispatchEvent(event.clone());
        }
    }

    private function writeToDisk():void {
        var fileData:ByteArray = new ByteArray();
        urlStream.readBytes(fileData, 0, urlStream.bytesAvailable);
        fileStream.writeBytes(fileData,0,fileData.length);
        waitingForDataToWrite = false;

        dispatchEvent(new DataEvent(DataEvent.DATA));
    }

    private function writeProgressHandler(evt:OutputProgressEvent):void{
        waitingForDataToWrite = true;
    }

    private function onCompleteEvent(event:Event):void {
        if(urlStream.bytesAvailable>0)
            writeToDisk();
        fileStream.close();

        fileStream.removeEventListener(OutputProgressEvent.OUTPUT_PROGRESS, writeProgressHandler);

        dispatchEvent(event.clone());
        // dispatch additional DownloadEvent
        dispatchEvent(new DownloadEvent(DownloadEvent.DOWNLOAD_COMPLETE, url, file));
    }

}
like image 812
Tom Solacroup Avatar asked Aug 03 '15 09:08

Tom Solacroup


1 Answers

Try an int variable that stores expected filesize..

When the Event Complete fires now check if your [download] fileData.length is equal to expected_FileSize.

If less then retry until equal or whatever is good enough. To get from last bytes amount you use a "range request" in the request header. It's also shown in this link If that helps you.

Something like URLRequestHeader("range","bytes="+startPOS+"-"+endPOS); where startPOS is filedata.length+1 and endPos is expected_FileSize amount.

like image 171
VC.One Avatar answered Oct 06 '22 05:10

VC.One