locked
File/Stream async methods failed in a loop

    Question

  • Hi guys,

    Does anyone tried call file / stream async methods in a loop like xxArray.forEach() or xxArray.map()?

    Here's a sample:

    function createThumbnailFiles() {
        var promises = [];
        var appData = Windows.Storage.ApplicationData.current;
        var localFolder = appData.localFolder;
    
        var picker = new Windows.Storage.Pickers.FileOpenPicker();
    
        picker.fileTypeFilter.replaceAll([".jpg", ".png", ".bmp"]);
        return picker.pickMultipleFilesAsync().then(function (files) {
    
            promises = files.map(function (file) {
                //return file.copyAsync(localFolder, file.name + file.fileType, Windows.Storage.CreationCollisionOption.replaceExisting).then(function () {
                return file.getThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.picturesView).then(function (stream) {
                    
                    return localFolder.createFileAsync(file.name + file.fileType, Windows.Storage.CreationCollisionOption.replaceExisting).then(function (outFile) {
                        return outFile.openAsync(Windows.Storage.FileAccessMode.readWriteNoCopyOnWrite).then(function (outFileStream) {
                            //console.log(stream.size);
                            var inputStream = stream.getInputStreamAt(0);
                            var outputStream = outFileStream.getOutputStreamAt(0);
    
                            var dataReader = new Windows.Storage.Streams.DataReader(inputStream);
                            var dataWriter = new Windows.Storage.Streams.DataWriter(outputStream);
    
                            return dataReader.loadAsync(stream.size).then(function () {
                                var bytes = new Array(stream.size);
                                dataReader.readBytes(bytes);
                                dataWriter.writeBytes(bytes);
                                //dataWriter.writeBuffer(dataReader.readBuffer(stream.size));
                                return dataWriter.storeAsync().then(function () {
                                    return outputStream.flushAsync().then(function () {
                                        //console.log("flushAsync() " + file.name + " done.");
                                    },
                                    function (e) {
                                        console.log("==== Error: " + e.message + " flushAsync() " + file.name + "failed.");
                                    });
                                },
                                function (e) {
                                    console.log("==== Error: " + e.message + " storeAsync() " + file.name + "failed.");
                                });
                            },
                            function (e) {
                                console.log("==== Error: " + e.message + " loadAsync() " + file.name + "failed.");
                            });
                        },
                        function (e) {
                            console.log("==== Error: " + e.message + " openAsync() " + file.name + "failed.");
                        });
                    },
                    function (e) {
                        console.log("==== Error: " + e.message + " createFileAsync() " + file.name + "failed.");
                    });
                },
                function(e) {
                    console.log("==== Error: " + e.message + " getThumbnailAsync() " + file.name + "failed.");
                });
            });
    
            return WinJS.Promise.join(promises).then(function () {
                console.log("All promises done.");
            },
            function (error) {
                console.log("Some promises not done.");
            });
        });
    }
    

    I pick up some image files via file picker, then get the thumbnails and write them to  new files under app local folder.

    When the file count was small(< 50), it's OK. But when I picked up 125 files, some files were not flushed correctly(size = 0).

    I found that the storeAsync(), flushAsync() failed with the error - The system can not find the file specified.

    But if I replace all code in the loop with "copyAsync()", it's OK for all 125 files.

    Can anyone help on this, thanks!

    Wednesday, December 28, 2011 7:01 AM

Answers

  • Hey Rocky,

    I was able to repro this with simpler code:

        function goClick()
            {
                var appData = Windows.Storage.ApplicationData.current;
                var localFolder = appData.temporaryFolder;
    
                var picker = new Windows.Storage.Pickers.FileOpenPicker();
    
                picker.fileTypeFilter.replaceAll([".jpg", ".png", ".bmp"]);
                picker.viewMode = Windows.Storage.Pickers.PickerViewMode.list;
                picker.pickMultipleFilesAsync().then(function (files) {
                    console.log("pickAsync() " + files.size);
                    files.map(function (file) {
                        console.log("files.map() " + file.name);
                        file.getThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.picturesView).then(function (stream) {
                            console.log("thumb() " + file.name);
                        });
                    });//jeff                
                });
            }
    
    

    I am going to file a bug on this.  Thanks for reporting the problem!

     

    -Jeff


    Jeff Sanders (MSFT)
    Thursday, December 29, 2011 8:02 PM
    Moderator

All replies

  • Hi Rocky,

    I do not understand the return statements in your function.  How are they supposed to affect the program flow and what is their purpose?

    -Jeff


    Jeff Sanders (MSFT)
    Wednesday, December 28, 2011 2:32 PM
    Moderator
  • Hi Rocky,
    I do not understand the return statements in your function.  How are they supposed to affect the program flow and what is their purpose?
    -Jeff

    Jeff Sanders (MSFT)

    Hi Jeff,
    The callback of files.map() will return a promise chained with each async file / stream call.
    So you can get a promise array returned by files.map().
    Then use WinJS.Promise.join() to chain all promises together to notify all files are done.
    ============================================
    promises = [];
    promises = files.map(function(eachFile) {
        ....
        return xxxAsync().then(function () {
            return yyyAsync().then(function() {
                ...
            });
        });
        ....
    });
    return WinJS.Promise.join(promises);
     ============================================
     
    You can ignore the return statements and the question is simple:
    I get a file array returned by the file open picker, and I want to do the same thing with each file(Eg: get the thumbnail and write to a new file).
     
     
    1. Uncomment "Debug.enableFirstChanceException(true);"  
    2. Make async file / stream calls in the array.forEach() or array.map() loop.
    3. I will get the "System can not find the file specified" exception when there're many flushAsync() calls at the same time(125 image files).
     
    You can remove all return statements to test:
    function createThumbnailFiles() {
        var appData = Windows.Storage.ApplicationData.current;
        var localFolder = appData.temporaryFolder;
    
        var picker = new Windows.Storage.Pickers.FileOpenPicker();
    
        picker.fileTypeFilter.replaceAll([".jpg", ".png", ".bmp"]);
        picker.viewMode = Windows.Storage.Pickers.PickerViewMode.list;
        picker.pickMultipleFilesAsync().then(function (files) {
    
            files.map(function (file) {
                //return file.copyAsync(localFolder, file.name + file.fileType, Windows.Storage.CreationCollisionOption.replaceExisting).then(function () {
                file.getThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.picturesView).then(function (stream) {
                    
                    localFolder.createFileAsync(file.name + file.fileType, Windows.Storage.CreationCollisionOption.replaceExisting).then(function (outFile) {
                        outFile.openAsync(Windows.Storage.FileAccessMode.readWriteNoCopyOnWrite).then(function (outFileStream) {
                            var inputStream = stream.getInputStreamAt(0);
                            var outputStream = outFileStream.getOutputStreamAt(0);
    
                            var dataReader = new Windows.Storage.Streams.DataReader(inputStream);
                            var dataWriter = new Windows.Storage.Streams.DataWriter(outputStream);
    
                            dataReader.loadAsync(stream.size).then(function () {
                                var bytes = new Array(stream.size);
                                dataReader.readBytes(bytes);
                                dataWriter.writeBytes(bytes);
    
                                dataWriter.storeAsync().then(function () {
                                    outputStream.flushAsync().then(function () {
                                        console.log("flushAsync() " + file.name + " done.");
                                    });
                                });
                            });
                        });
                    });
                });
            });                
        });
    }
    
           
    Thursday, December 29, 2011 4:43 AM
  • Hey Rocky,

    I was able to repro this with simpler code:

        function goClick()
            {
                var appData = Windows.Storage.ApplicationData.current;
                var localFolder = appData.temporaryFolder;
    
                var picker = new Windows.Storage.Pickers.FileOpenPicker();
    
                picker.fileTypeFilter.replaceAll([".jpg", ".png", ".bmp"]);
                picker.viewMode = Windows.Storage.Pickers.PickerViewMode.list;
                picker.pickMultipleFilesAsync().then(function (files) {
                    console.log("pickAsync() " + files.size);
                    files.map(function (file) {
                        console.log("files.map() " + file.name);
                        file.getThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.picturesView).then(function (stream) {
                            console.log("thumb() " + file.name);
                        });
                    });//jeff                
                });
            }
    
    

    I am going to file a bug on this.  Thanks for reporting the problem!

     

    -Jeff


    Jeff Sanders (MSFT)
    Thursday, December 29, 2011 8:02 PM
    Moderator
  • Hey Rocky,

    I was able to repro this with simpler code:

     

        function goClick()
            {
                var appData = Windows.Storage.ApplicationData.current;
                var localFolder = appData.temporaryFolder;
    
                var picker = new Windows.Storage.Pickers.FileOpenPicker();
    
                picker.fileTypeFilter.replaceAll([".jpg", ".png", ".bmp"]);
                picker.viewMode = Windows.Storage.Pickers.PickerViewMode.list;
                picker.pickMultipleFilesAsync().then(function (files) {
                    console.log("pickAsync() " + files.size);
                    files.map(function (file) {
                        console.log("files.map() " + file.name);
                        file.getThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.picturesView).then(function (stream) {
                            console.log("thumb() " + file.name);
                        });
                    });//jeff                
                });
            }
    
    

    I am going to file a bug on this.  Thanks for reporting the problem!

     

     

    -Jeff


    Jeff Sanders (MSFT)


    Thank you, Jeff:)

    In my opinion, MSFT should provide both async and sync APIs in WinRT for JS.

    Developers can make their own async method with MSFT's sync APIs and WinJS.Promise.

    This issue will be resolved easily in C# with the simple "await".


    BR,

    Frank Xu

     

    • Proposed as answer by tatatee Thursday, August 02, 2012 3:18 AM
    Friday, December 30, 2011 3:11 AM