I'm writing an AIR kiosk app that every night connects to a WordPress server, gets a JSON file with paths to all the content, and then downloads that content and saves it to the kiosk hard drive.
There's several hundred files (jpg, png, f4v, xml) and most of them download/save with no problems. However, there are two f4v files that never get downloaded completely. The complete event does get dispatched, but if I compare the bytesTotal (from the progress event) vs bytesAvailable (from the complete event) they don't match up; bytesTotal is larger. The bytesTotal (from the progress event) matches the bytes on the server.
The bytesLoaded in the progress event never increases to the point that it matches the bytesTotal either so I can't rely on the progress event either. This seems to happen on the same two videos every time. The videos are not very large, one is 13MB and the other is 46MB. I have larger videos that download without any problems.
EDIT: After rebooting my computer, the two videos now finish downloading but I'm getting the same problem with a 300kb png file.
If I paste the url into Firefox it downloads correctly. I've also written a simple c# app to download the files and it is able to download them with no problems, so it appears to be a problem with Flash/AIR.
EDIT: here's a simpler version of the code, this is from a test project and it's the only code (the url is on our local network so you won't be able to download the file yourself):
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.ProgressEvent;
import flash.net.URLRequest;
import flash.net.URLStream;
[SWF(backgroundColor="#000000", frameRate="24", width="640", height="480")]
public class Test extends Sprite {
private var fileSize:Number;
private var stream : URLStream;
private var url:String = "http://192.168.150.219/wordpress2/wp-content/uploads/2012/12/John-Butler-clip1.f4v";
public function Test() {
if (stage)
init();
else
this.addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event=null):void {
this.removeEventListener(Event.ADDED_TO_STAGE, init);
stream = new URLStream();
stream.addEventListener(ProgressEvent.PROGRESS, onLoadProgress);
stream.addEventListener(Event.COMPLETE, onLoadComplete);
stream.load(new URLRequest(url));
}
private function onLoadProgress(event:ProgressEvent):void {
fileSize = event.bytesTotal;
var percent:Number = event.bytesLoaded / event.bytesTotal * 100;
trace(percent + "%"); // this never gets to 100%
}
private function onLoadComplete(event:Event):void {
trace("loaded", stream.bytesAvailable, "of", fileSize);
// outputs "loaded 13182905 of 13184365"
// so why is it "complete" when it isn't fully downloaded?
}
}
}
Your answer is in your question.
Normal URLs (files) - to this server this is a block of data. Once the server delivers the 'block of data' the delivery process is considered 'COMPLETE'. In this case if a file is 100kb, once the 100kb is received - Flash considers this as 'COMPLETE'.
URLStream - to the server this is [TWO] blocks of data (very simple way to look at it). The server will first serve the CONNECTION to the stream... then serve the STREAM DATA. This is handled in Flash just as its described.
Flash will consider the loading of the CONNECTION as 'COMPLETE', and NEVER check if the STREAM data is loaded - thats up to your server. In any streams you should actually check the [load progress] event and read each byte of data as it comes in... then construct as required.
If anyone else has the same problem like I did. It turned out to be a caching problem which is present in AIR as well so a timestamp added to the request solves this: http://www.newtonflash.com/blog/as3/prevent-xml-caching-problem/#comment-43
Don't compare to
bytesAvailable
, uselength
instead. BytesAvailable is actuallyByteArray.length - ByteArray.position
. So if the position within the ByteArray has moved away from index 0, the bytesAvailable value will decrease. Length will always be the total number of bytes within the array.Try comparing using length and see if this makes any difference. I don't have time to sift through your code to see if you are changing position at any point (either purposefully or accidentally; you can do it in more ways than one), so that's the best I can offer right now.