Re-uploading a file with AJAX after it was changed causes net::ERR_UPLOAD_FILE_CHANGED in Chrome

37,714

Solution 1

A quick workaround would be to include a complete parameter to AJAX call (or any equivalent of a finally call that always gets invoked) and add code that resets the form. I've noticed that attaching the file again solves it. While I appreciate it's not a real solution to the problem, it does provide handling or rather avoidance of that error.

This way, if the user for any reason needs to re-upload the file, they'll have to choose the file again.

Example below:

$.ajax({
    url: this.action,
    type: this.method,
    data: this.data,
    success: function (response) { 
        // success scenario
    },
    error: function (result) {
        // error scenario
    },
    complete: function (data) {
        $('#uploadForm')[0].reset(); // this will reset the form fields
    }
});

Solution 2

I managed to get a more descriptive error using the javascript File Reader. After the the file is changed and the button is pressed for the second time, the FileReader throws a somewhat more descriptive message:

DOMException: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.

At least this is an error that can be caught and displayed to the user, so they are aware that they need to re-select the file.

Here is the snippet:

<html>
    <head>
        <title>Test</title>
        <script type='text/javascript'>
        document.addEventListener("DOMContentLoaded", function(){

            const button = document.querySelector('#btnSubmit');

            button.addEventListener('click', () => {
                let inputElement = document.getElementById('csvfile');

                for (let i = 0; i < inputElement.files.length; i++) {
                    const file = inputElement.files[i];

                    console.log(file);

                    const reader = new FileReader();
                    reader.readAsText(file, "UTF-8");

                    reader.onload = function (evt) {
                        console.log(evt.target.result);
                    }

                    reader.onerror = function (err) {
                        // read error caught here
                        console.log(err);
                        console.log(err.target.error);
                    }

                }

            });
        });
        </script>

    </head>
    <body>
        <form enctype="multipart/form-data" action="" method="post" id='theForm'>
                <input type="file" name="csvfile" id="csvfile" value="" />
                <input type="button" name="uploadCSV" value="Upload" id='btnSubmit'/>
        </form>
    </body>
</html> 

It seems that there is no way around this at the moment (June 2020). There is an open bug on Chromium:

https://bugs.chromium.org/p/chromium/issues/detail?id=1084880&q=ERR_UPLOAD_FILE_CHANGED&can=2

Solution 3

Just reset the input type file after prompt messages or processing the data

$('#file').val("");

Solution 4

No, if the user has changed file it already another file so you can't (should not) get access to this file without user explicit user action otherwise it would be a security issue.

You can save the file in the memory when user uploads it.


document.getElementById('fileInput').addEventListener('change', function() {
   saveFileConentInMemory(this.files[0].arrayBuffer());
});

And when the user press the "Send" button just get this content from memory and send it

button.addEventListener('click', () => {
   const file = getFileContentFromMemeory();
   send(file);
})

Yes, you can't be sure that you send the latest version of the file but you should be sure that you send the content which was uploaded.

Also, you should aware of memory consumption and asynchronous API of reading file (so you still can get this error about changed content even when you write it in memory)

Share:
37,714
Martin Taleski
Author by

Martin Taleski

IT enthusiast

Updated on May 07, 2021

Comments

  • Martin Taleski
    Martin Taleski about 3 years

    I have a simple HTML form that sends an selected file via AJAX to an API endpoint.

    If I do the following steps:

    1. Press the Upload button and make an POST request to upload the file
    2. Change the selected file on the filesystem (in a text editor), without picking it again in the file picker.
    3. Press the Upload button again, and make as second POST request

    Chrome will fail the second request with an error: net::ERR_UPLOAD_FILE_CHANGED. Note that if you change the file before the initial upload, the file will be uploaded without a problem. The error happens only on the second upload, when you change the file after an initial successful upload. I am testing this with CSV files, and changing them in a text editor.

    There does not seem to be a way to isolate that error.

    Is there any way around this?

    If not, is it possible to catch this specific error, and display a meaningful message to the user. The error that Fetch returns in the promise has no specific info about this. The only place where I see the ERR_UPLOAD_FILE_CHANGED is in the browser dev console.

    I am pretty sure that there was not any problem with this about a year ago (early 2019), as the possibility to re-upload a changed file, played in nicely in our UI flow. Now we need to force the user to pick the file again. So my assumption that this was introduced with a recent chrome update.

    Here is a simplified snippet of the code:

    <html>
    <head>
            <script type='text/javascript'>
            document.addEventListener("DOMContentLoaded", function(){
    
                    const button = document.querySelector('#btnSubmit');
    
                    button.addEventListener('click', () => {
                            const form = new FormData(document.querySelector('#theForm'));
                            const url = '/my-api'
                            const request = new Request(url, {
                                    method: 'POST',
                                    body: form
                            });
            
                            fetch(request).then(function() {
                                    console.log("ok");
                            }).catch(function(err) {
                                    console.log(err);
                            });
                    });
            });
            </script>
    
    </head>
    <body>
            <form enctype="multipart/form-data" action="" method="post" id='theForm'>
                    <input type="file" name="csvfile" id="csvfile" value="" /></td>
                    <input type="button" name="uploadCSV" value="Upload" id='btnSubmit'/>
            </form>
    </body>
    </html>
    

    EDIT: The bug is marked as WontFix on bugs.chromium.org: https://bugs.chromium.org/p/chromium/issues/detail?id=1086707#c7

  • Martin Taleski
    Martin Taleski almost 4 years
    After the file is changed, file.lastModified is still being shown as the old last modified date. It seems that the browser does not have permission to read the file after the change. See my answer bellow: stackoverflow.com/a/62141237/855475
  • Jammeth_Q
    Jammeth_Q almost 4 years
    I can understand the potential security issues involved with this, but I have an app that displays visualizations of numerical model results that are located on the users local machine. Is there any hope for "refreshing" the file contents without another file selection dialog?
  • Jammeth_Q
    Jammeth_Q almost 4 years
    Being able to simply "refresh" the local file contents was an important part of my visualization app. This is a bummer.
  • Kaiido
    Kaiido almost 4 years
    An easier and more performant check can be made with file.slice(0,1).arrayBuffer() as demonstrated here.
  • Vitalij
    Vitalij almost 4 years
    @maksimr, your explanation has no sense... You do realize that it's possible to attach the file to the file input, change the content of this file and then upload changed file without any issues? It's reuploading the file, changed from the previous upload, what throws an error.