locked
"Resource access is restricted for cross-origin URL" warning for a blob of a local file?

    Question

  • I'm not one to usually post errors I'm getting and ask for help, but considering I couldn't find any reference to this error on the web, I could really use some backup!

    Essentially, I have an image file that has just been downloaded from the web and saved to the Windows.Storage.ApplicationData.current.localFolder. I call URL.createObjectURL(theImageFile, {oneTimeOnly: false}) to get an object url that looks something like "blob:172BA7C8-062F-4549-A0B3-37683B2C5849". I then set the src attribute on an img HTML element to this object url, and instead of the image element displaying the image whose object url I passed it, it shows a red x. If I look in the console in Visual Studio at this point, I see I am getting a warning: "Access Denied. Resource access is restricted for cross-origin URL: 'blob:172BA7C8-062F-4549-A0B3-37683B2C5849'".

    This error might make sense if the image file I was trying to display was on a different internet domain, but I don't understand why a file in the localFolder of my app would have this kind of access restriction. What's weirder is that if I close the app and open it again, the image shows up in the image element just fine. I only get the red x and warning right after downloading the image.

    Appreciate any help!

    • Edited by Joe Levy_ Monday, August 13, 2012 3:08 PM
    Monday, August 13, 2012 3:04 PM

Answers

  • Thanks Joe,

    My code also works fine for RP bits.

    The repro is always the key to these situations...

    Here is how to do this in RP with your sample:

      openPicker.pickSingleFileAsync().then(function (photo) {
                if (photo) {
                    savePhotoLocally(photo, function (err, file) {
                        if (!err) {
                            
                            var elements = document.querySelectorAll(".img");
                            for (var i = 0; i < elements.length; i++) {
                                msSetImmediate(setSrc(elements[i],file));
                            }
    
                            
                            
                        }
                    });
                }
            });
            }
    
            function setSrc(theElem,file){
                theElem.src = "ms-appdata:///local/" + file.name; 
            }
    -Jeff


    Jeff Sanders (MSFT)

    Wednesday, August 15, 2012 7:02 PM
    Moderator

All replies

  • Hi Joe,

    What is your code doing?  I do this without a problem.  Can you put together a simple repro of the problem?  PS:

    Try setting the src directly something like this:

                        // Copy the stream from the blob to the File stream 
                        Windows.Storage.Streams.RandomAccessStream.copyAsync(input, output).then(function () { 
                            output.flushAsync().done(function () { 
                                input.close(); 
                                output.close(); 
                                //WinJS.log && WinJS.log("File '" + file.name + "' saved successfully to the Pictures Library!", "sample", "status");
                                msSetImmediate(function () { xhrImg.src = "ms-appdata:///local/" + file.name; });
                            }); 
                        }); 

     

    -Jeff


    Jeff Sanders (MSFT)


    Monday, August 13, 2012 7:20 PM
    Moderator
  • Jeff, I will try to produce some code to repro this problem simply, but regarding using:
     xhrImg.src = "ms-appdata:///local/" + file.name;

    I was under the impression that the src attribute of image tags can only use the following due to security concerns: 
    xhrImage.src = URL.createObjectURL(file);

    I actually tried using the first method much in the past to no avail, and was only able to get images to actually display using the second method.


    • Edited by Joe Levy_ Monday, August 13, 2012 9:06 PM
    Monday, August 13, 2012 9:05 PM
  • Hi Joe,

    Yes it works (I am using the RTM bits).  You can download this version tomorrow if you have an MSDN or TechNet subscription.

    -Jeff


    Jeff Sanders (MSFT)

    Tuesday, August 14, 2012 12:05 PM
    Moderator
  • Don't have access to MSDN or TechNet quite yet.

    Do you know if that was a change from RP to RTM? I haven't been able to get this working in the RP version:

    xhrImg.src = "ms-appdata:///local/" + file.name;

    Here's my simple repro of the original issue:

    Create a new project using the 'Grid App' javascript template.

    Replace default.html with:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <title>App3</title>
    
        <!-- WinJS references -->
        <link href="//Microsoft.WinJS.1.0.RC/css/ui-dark.css" rel="stylesheet" />
        <script src="//Microsoft.WinJS.1.0.RC/js/base.js"></script>
        <script src="//Microsoft.WinJS.1.0.RC/js/ui.js"></script>
    
        <!-- App3 references -->
        <link href="/css/default.css" rel="stylesheet" />
        <script src="/js/data.js"></script>
        <script src="/js/navigator.js"></script>
        <script src="/js/default.js"></script>
    </head>
    <body>
        <img class="img" />
        <img class="img" />
        <img class="img" />
        <button id="button">Pick Image</button>
    </body>
    </html>

    Replace default.js with:

    // For an introduction to the Grid template, see the following documentation:
    // http://go.microsoft.com/fwlink/?LinkID=232446
    (function () {
        "use strict";
    
        var app = WinJS.Application;
        var activation = Windows.ApplicationModel.Activation;
        var nav = WinJS.Navigation;
        WinJS.strictProcessing();
    
        app.addEventListener("activated", function (args) {
            if (args.detail.kind === activation.ActivationKind.launch) {
                if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                    // TODO: This application has been newly launched. Initialize
                    // your application here.
                } else {
                    // TODO: This application has been reactivated from suspension.
                    // Restore application state here.
                }
    
                if (app.sessionState.history) {
                    nav.history = app.sessionState.history;
                }
                args.setPromise(WinJS.UI.processAll().then(function () {
                    if (nav.location) {
                        nav.history.current.initialPlaceholder = true;
                        return nav.navigate(nav.location, nav.state);
                    } else {
                        //return nav.navigate(Application.navigator.home);
                    }
                }));
    
                document.querySelector("#button").onclick = selectContact;
            }
        });
    
        app.oncheckpoint = function (args) {
            // TODO: This application is about to be suspended. Save any state
            // that needs to persist across suspensions here. If you need to 
            // complete an asynchronous operation before your application is 
            // suspended, call args.setPromise().
            app.sessionState.history = nav.history;
        };
    
        app.start();
    
        function selectContact() {
            var currentState = Windows.UI.ViewManagement.ApplicationView.value;
            if (currentState === Windows.UI.ViewManagement.ApplicationViewState.snapped && !Windows.UI.ViewManagement.ApplicationView.tryUnsnap()) {
                return;
            }
    
            var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
            openPicker.commitButtonText = "Use";
            openPicker.viewMode = Windows.Storage.Pickers.PickerViewMode.thumbnail;
            openPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
            openPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg", ".bmp"]);
    
            openPicker.pickSingleFileAsync().then(function (photo) {
                if (photo) {
                    savePhotoLocally(photo, function (err, file) {
                        if (!err) {
                            var url = URL.createObjectURL(file, {oneTimeOnly: false});
    
                            var elements = document.querySelectorAll(".img");
                            for(var i = 0; i < elements.length; i ++) {
                                elements[i].src = url;
                            }
                        }
                    });
                }
            });
        }
    
        function savePhotoStreamLocally(photoStream, callback) {
            var localFolder = Windows.Storage.ApplicationData.current.localFolder;
            var fileName = "photo.png";
            var onExists = Windows.Storage.CreationCollisionOption.replaceExisting;
    
            localFolder.createFileAsync(fileName, onExists).then(function (tempFile) {
                saveStreamToFile(tempFile, photoStream, callback);
            },
            function (err) {
                callback(err);
            });
        }
    
        function savePhotoLocally(photo, callback) {
            photo.openAsync(Windows.Storage.FileAccessMode.read).then(function (stream) {
                savePhotoStreamLocally(stream, callback);
            },
            function (err) {
                callback(err);
            });
        }
    
        function saveStreamToFile(file, stream, callback) {
            file.openAsync(Windows.Storage.FileAccessMode.readWrite).then(function (saveStream) {
                var outputStream = saveStream.getOutputStreamAt(saveStream.size);
                var writer = new Windows.Storage.Streams.DataWriter(outputStream);
                var reader = new Windows.Storage.Streams.DataReader(stream.getInputStreamAt(0));
    
                reader.loadAsync(stream.size).then(function () {
                    while (reader.unconsumedBufferLength > 0) {
                        writer.writeByte(reader.readByte());
                    }
    
                    writer.storeAsync().then(function () {
                        outputStream.flushAsync().then(function () {
                            saveStream.close();
                            stream.close();
                            callback(null, file);
                        },
                        function (err) {
                            callback(err);
                        });
                    },
                    function (err) {
                        callback(err);
                    });
                },
                function (err) {
                    callback(err);
                });
            },
            function (err) {
                callback(err);
            });
        }
    
    })();


    Click 'pick image' to select an image from the file picker. After you pick, it should populate the img tags next to it with a copy of the image you picked, that has been saved to the current app's local folder. Instead, it puts a red x in each image and in the console says "Access Denied. Resource access is restricted for cross-origin URL: 'blob:77257E8A-8BF2-474A-9B9D-22C2388A6AD0'."




    • Edited by Joe Levy_ Wednesday, August 15, 2012 5:52 PM
    Wednesday, August 15, 2012 3:41 PM
  • Thanks Joe,

    My code also works fine for RP bits.

    The repro is always the key to these situations...

    Here is how to do this in RP with your sample:

      openPicker.pickSingleFileAsync().then(function (photo) {
                if (photo) {
                    savePhotoLocally(photo, function (err, file) {
                        if (!err) {
                            
                            var elements = document.querySelectorAll(".img");
                            for (var i = 0; i < elements.length; i++) {
                                msSetImmediate(setSrc(elements[i],file));
                            }
    
                            
                            
                        }
                    });
                }
            });
            }
    
            function setSrc(theElem,file){
                theElem.src = "ms-appdata:///local/" + file.name; 
            }
    -Jeff


    Jeff Sanders (MSFT)

    Wednesday, August 15, 2012 7:02 PM
    Moderator
  • Thanks Jeff, that makes my simple app repro work correctly. However, in my actual app I use a WinJS.Binding.List to bind a wincontrol and ui template to a datasource.

    So I don't have:

    theElem.src = "ms-appdata:///local/" + file.name; 

    in my code, but instead have something like:

    var item = {

    "src": "ms-appdata:///local/" + file.name,

    ...

    };

    messagesListBinding.setAt(index, item);

    where messagelistBinding's datasouce is the datasource for a winControl that also has an item template. Given this scenario, I'm not really sure what exactly I should be wrapping in msSetImmediate to allow the image to display.

    I also don't really understand what msSetImmediate does considering, in the simple repro, the file should be finished saving before msSetImmediate is even called, so I'm not sure what msSetImmediate is waiting on that causes the simple repro to work.

    Thursday, August 16, 2012 7:46 AM
  • Actually, let's ignore my last post for a second.

    In the simple repro I gave you, even with it changed to your code, it still doesn't work for all images. Seems to only work when png images are picked, otherwise it just shows the red x again. I think what the issue is is that the simple repro (and my app as well) are saving the file with a ".png" extension even if it is a jpeg, etc. I think: 

     theElem.src = "ms-appdata:///local/" + file.name; 

    or even

    <img src="ms-appdata:///local/someFile" /> 

    fails silently (and shows a red x) if it is given an image with an extension that does not actually match the encoding of the image's contents. Ex: a ".png" file which actually is encoded as a jpeg.

    URL.createObjectURL(file);

    seems to work regardless of a mismatch, which is why in the past I always had to use URL.createObjectURL to get it to work in my app... because my image encodings did not match the file extension.

    However, URL.createObjectURL seems to show a red x sometimes, and leave " Access Denied. Resource access is restricted for cross-origin URL" in the console as a warning, but for a different reason then encoding / file extension mismatches, as in the simple repro code, changing your setSrc function to:

     function setSrc(theElem,file){
          theElem.src = URL.createObjectURL(file); 
    }

    will always show red x's and the console warning, even if the file I pass it is png encoded with a png extension (no mismatch).

    It may be a good idea for some error / warning to be thrown rather than just a red x to be shown when a mismatch happens, since both the win8 file system, the Paint program, and URL.createObjectURL correctly show the image regardless of a mismatch (although, as shown above, URL.createObjectURL seems to have some other issue, unrelated to mismatches). Made it very hard for me to find that anything was wrong since in most places the image displayed correctly regardless of the mismatch.


    • Edited by Joe Levy_ Thursday, August 16, 2012 5:19 PM
    Thursday, August 16, 2012 5:14 PM
  • OK, if I understand you correctly... You simply need to test for the file type before saving and everything is OK correct?

    Jeff Sanders (MSFT)

    Thursday, August 16, 2012 6:03 PM
    Moderator
  • Yup, thanks for the help. Amazing how such a tiny issue can cause so much pain!
    Friday, August 17, 2012 1:25 AM