3

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?
        }
    }
}
Dave Wolfe
  • 660
  • 5
  • 14
  • hi! the code is too long to understand in a few minutes.. next time please only include the parts where is the trouble.. a) in loadNext() you dispatch the event, but you listen to it on mEventManager, not this. If thats okay, you can edit onStreamProgress() to check if (e.loadedbytes == e.bytestotal), and call the complete function manually. Hope it helped, I really just peaked to the code. – csomakk Jan 24 '13 at 12:38
  • Well I'm not entirely sure where the problem is :) The complete event I'm dispatching is not the one I'm listening to in the init() method, that one is added to the URLStream. When all the items in the queue are finished downloading/saving I then dispatch a complete event that another class is listening to. – Dave Wolfe Jan 24 '13 at 18:20

3 Answers3

2

Don't compare to bytesAvailable, use length instead. BytesAvailable is actually ByteArray.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.

Josh
  • 8,079
  • 3
  • 24
  • 49
  • I'm not changing the position of the byte array at any point. When the URLStream dispatches the complete event I read the whole thing into a new byte array and then save that byte array to disk. Also, neither URLStream nor ProgressEvent have a length property, bytesLoaded, bytesTotal, and bytesAvailable are all I have to work with. – Dave Wolfe Jan 24 '13 at 18:23
  • Oh, you're right. I'm used to immediately writing the data to a ByteArray after the COMPLETE event fires and checking the length property on the ByteArray. Sorry about that. Only other thing I could think of was a quick write of the data at some point, but I checked and you do not do that until the COMPLETE event fires. I am at a loss as to why this happens. – Josh Jan 24 '13 at 20:07
2

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

{
var xmlPath:String="replaceYourXMLPathHere.xml"
var urlReq:URLRequest = new URLRequest(xmlPath+"?time=" + new Date().getTime());
}
baivulcho
  • 21
  • 1
  • This might have done the trick, unfortunately I can't test it now, the application has shipped. I ended up using NativeProcess and a c# console app to do the downloading. I'm not 100% the time stamp would work anyway since I experienced the problem with files that hadn't previously been downloaded. – Dave Wolfe Mar 20 '13 at 23:27
0

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.

Reshape Media
  • 341
  • 3
  • 11
  • In the first version of the class I posted before the edit, I did write the stream to disk as it comes in during the progress event, but it still never finished. If I ignored the complete event and just go by bytesLoaded/bytesTotal, it never finishes because bytesLoaded is always less than bytesTotal, and I stop getting progress events once I get to about 99.98% finished. I wrote a quick c# app that downloads the file and it works correctly, with the progress event reaching 100%. This leads me to believe the problem lies with Flash/AIR. – Dave Wolfe Jan 24 '13 at 23:57
  • Also wanted to add, according to the AS3 documentation, URLStream is not supposed to dispatch the complete event until the data is downloaded successfully. This is how it works with all the hundreds of other files I'm downloading, it's just two particular videos that AIR seems incapable of downloading completely. – Dave Wolfe Jan 25 '13 at 00:03
  • Sorry I was a bit miss leading when I said a stream connect will be considered 'COMPLETE'. Flash will NEVER fire the complete event for a stream be cause of the nature of a stream - aka - its never complete until the sever serves the terminating byte. In code you should listen to the url progress event and send pointers to note when the file transfer begins/ends. Technically you could keep a URLStream open forever if the server can handle it - hence why Flash will always think the URLStream is still loading and not 'COMPLETE' – Reshape Media Jan 25 '13 at 16:00
  • 1
    Flash DOES fire the complete event for a URLStream. If you read the API docs you'll see that when the data transfer is completed successfully, the complete event is fired. The complete event is working as expected for the most of the files I'm downloading. As I stated earlier, I can't use the ProgressEvent because, for the files that fail to download, the bytesLoaded is always less than bytesTotal. It generally reaches about 99.98% and then no more ProgressEvents are fired. – Dave Wolfe Jan 25 '13 at 19:14
  • @Dave Wolfe It seems I miss-took what you were asking. I was under the impression you were streaming content similar to a live socket connection, which is required to be open. Am I to understand you are only serving files to a client machine? Also in the progress event, you are not concerned about the bytes total, only the bytes loaded. Its up to you to construct/deserialize the loaded bytes. – Reshape Media Jan 26 '13 at 05:33
  • Why would I ignore bytesTotal? That's how I know how much progress I've made in my progress event. It's the total number of bytes that I need to download, so when bytesLoaded == bytesTotal then I know the download is complete; that's the whole reason bytesTotal exists in the first place. – Dave Wolfe Jan 26 '13 at 17:52