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));
}
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With