Javascript 更改后使用AJAX重新上载文件会导致net::ERR_UPLOAD_file_在Chrome中更改

Javascript 更改后使用AJAX重新上载文件会导致net::ERR_UPLOAD_file_在Chrome中更改,javascript,ajax,google-chrome,file-upload,Javascript,Ajax,Google Chrome,File Upload,我有一个简单的HTML表单,它通过AJAX将选定的文件发送到API端点 如果我执行以下步骤: 按下上载按钮并发出POST请求以上载文件 更改文件系统(在文本编辑器中)上的选定文件,而无需在文件选择器中再次拾取该文件 再次按下上载按钮,并作为第二个POST请求进行设置 Chrome将在第二次请求失败时出错:net::ERR\u UPLOAD\u FILE\u CHANGED。请注意,如果您在首次上载之前更改了文件,则将毫无问题地上载该文件。只有在首次成功上载后更改文件时,才会在第二次上载时发生此错

我有一个简单的HTML表单,它通过AJAX将选定的文件发送到API端点

如果我执行以下步骤:

  • 按下上载按钮并发出POST请求以上载文件
  • 更改文件系统(在文本编辑器中)上的选定文件,而无需在文件选择器中再次拾取该文件
  • 再次按下上载按钮,并作为第二个POST请求进行设置
  • Chrome将在第二次请求失败时出错:
    net::ERR\u UPLOAD\u FILE\u CHANGED
    。请注意,如果您在首次上载之前更改了文件,则将毫无问题地上载该文件。只有在首次成功上载后更改文件时,才会在第二次上载时发生此错误。我正在用CSV文件进行测试,并在文本编辑器中更改它们

    似乎没有办法孤立这一错误

    这有什么办法吗

    如果没有,是否可以捕获此特定错误,并向用户显示有意义的消息。承诺中Fetch返回的错误没有关于此的特定信息。在浏览器开发控制台中,我只能看到
    ERR\u UPLOAD\u FILE\u CHANGED

    我敢肯定,大约一年前(2019年初),这并没有任何问题,因为重新上传更改的文件的可能性,在我们的UI流中很好地发挥了作用。现在我们需要强制用户再次选择文件。所以我假设这是在最近的chrome更新中引入的

    以下是代码的简化片段:

    <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>
    
    
    document.addEventListener(“DOMContentLoaded”,function()){
    const button=document.querySelector(“#btnSubmit”);
    按钮。addEventListener('单击',()=>{
    const form=new FormData(document.querySelector(“#theForm”);
    常量url='/my api'
    const request=新请求(url{
    方法:“POST”,
    正文:表格
    });
    获取(请求).then(函数(){
    控制台日志(“ok”);
    }).catch(函数(err){
    控制台日志(err);
    });
    });
    });
    

    编辑:bug在bugs.chromium.org上被标记为WontFix:

    我只需在点击上传按钮和重置表单值之间检查file.lastModified


    更好的解决方案是将文件读入ArrayBuffer并上传。

    我使用javascript文件读取器获得了一个更具描述性的错误。更改文件并第二次按下按钮后,FileReader会抛出一条更具描述性的消息:

    DOMException:无法读取请求的文件,通常是由于获取文件引用后出现的权限问题。

    至少这是一个可以捕获并显示给用户的错误,因此用户知道需要重新选择文件

    以下是片段:

    <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> 
    
    
    试验
    document.addEventListener(“DOMContentLoaded”,function()){
    const button=document.querySelector(“#btnSubmit”);
    按钮。addEventListener('单击',()=>{
    让inputElement=document.getElementById('csvfile');
    for(设i=0;i
    目前似乎没有办法解决这个问题(2020年6月)。Chromium上有一个打开的bug:


    否,如果用户更改了文件,则该文件已经是另一个文件,因此在没有用户明确的用户操作的情况下,您无法(不应该)访问该文件,否则这将是一个安全问题

    用户上传文件时,可以将其保存在内存中

    
    document.getElementById('fileInput')。addEventListener('change',function(){
    saveFileConentInMemory(this.files[0].arrayBuffer());
    });
    
    当用户按下“发送”按钮时,只需从内存中获取内容并发送即可

    按钮。addEventListener('单击',()=>{
    const file=getFileContentFromMemeory();
    发送(文件);
    })
    
    是的,您不能确定是否发送了文件的最新版本,但应确保发送了上载的内容


    此外,您还应该了解读取文件的内存消耗和异步API(因此即使在内存中写入内容,也可能会出现有关更改内容的错误)

    一个快速的解决方法是在AJAX调用中包含一个
    complete
    参数(或总是被调用的任何等价的
    finally
    调用)并添加重置表单的代码。我注意到再次附加文件可以解决这个问题。虽然我理解这不是问题的真正解决方案,但它确实提供了处理,或者说避免了错误

    这样,如果用户出于任何原因需要重新上传文件,他们将不得不再次选择该文件

    示例如下:

    $.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
        }
    });
    

    只需在提示消息或处理
    $('#file').val("");
    
    $("#id_of_your_file_input").click(function(){
     $("#id_of_your_file_input").val('');
    });