【问题标题】:Reading multiple files with Javascript FileReader API one at a time使用 Javascript FileReader API 一次读取多个文件
【发布时间】:2012-12-08 03:24:26
【问题描述】:

我正在使用 FileReader API 读取多个文件。

<html> <body>
    <input type="file" id="filesx" name="filesx[]"
      onchange="readmultifiles(this.files)" multiple=""/>
    <div id="bag"><ul/></div>
<script>
window.onload = function() {
    if (typeof window.FileReader !== 'function') {
        alert("The file API isn't supported on this browser yet.");
    }
}

function readmultifiles(files) {
    var ul = document.querySelector("#bag>ul");
    while (ul.hasChildNodes()) {
        ul.removeChild(ul.firstChild);
    }

    function setup_reader(file) {
        var name = file.name;
        var reader = new FileReader();
        reader.onload = function(e) {
            var bin = e.target.result; //get file content

            // do sth with text

            var li = document.createElement("li");
            li.innerHTML = name;
            ul.appendChild(li);
        }
        reader.readAsBinaryString(file);
    }

    for (var i = 0; i < files.length; i++) { setup_reader(files[i]); }
}
</script> </body> </html>

问题是同时读取所有文件,当文件的总大小(总和)非常大时,浏览器会崩溃。

我想一个接一个地读取文件,这样可以减少内存消耗。

这可能吗?

【问题讨论】:

标签: javascript html filereader fileapi


【解决方案1】:

我自己想出了一个可行的解决方案。

function readmultifiles(files) {
  var reader = new FileReader();  
  function readFile(index) {
    if( index >= files.length ) return;
    var file = files[index];
    reader.onload = function(e) {  
      // get file content  
      var bin = e.target.result;
      // do sth with bin
      readFile(index+1)
    }
    reader.readAsBinaryString(file);
  }
  readFile(0);
}

【讨论】:

  • 为什么不为每次读取创建一个新的FileReader 对象?我相信 FileReader 对象的状态从0(空)更改为1(正在加载),然后再更改为2(完成)后,它不应该被重用。这可能会给您带来一些严重的浏览器错误。
  • 不错的递归函数。
  • 如果我使用“reader.addListener('load', callback)”。最后一项将是两倍。
【解决方案2】:

这应该会一一读取文件:

function readmultifiles(files) {
    var ul = document.querySelector("#bag>ul");
    while (ul.hasChildNodes()) {
        ul.removeChild(ul.firstChild);
    }
    // Read first file
    setup_reader(files, 0);
}

// Don't define functions in functions in functions, when possible.

function setup_reader(files, i) {
    var file = files[i];
    var name = file.name;
    var reader = new FileReader();
    reader.onload = function(e){
                        readerLoaded(e, files, i, name);
                    };
    reader.readAsBinaryString(file);
    // After reading, read the next file.
}

function readerLoaded(e, files, i, name) {
    // get file content  
    var bin = e.target.result;
    // do sth with text

    var li = document.createElement("li");
    li.innerHTML = name;
    ul.appendChild(li);

    // If there's a file left to load
    if (i < files.length - 1) {
        // Load the next file
        setup_reader(files, i+1);
    }
}

【讨论】:

  • 这是一个很好的选择,尤其是当您需要哪个文件已完成 onload 以便您可以获取 file.type 等时。
【解决方案3】:

为了新用户的利益,我更新了这个问题,他们正在寻找通过the FileReader API 上传多个文件的解决方案,尤其是使用 ES。

使用Object.keys(files) in ES 比手动迭代每个文件更简单、更简洁:

<input type="file" onChange="readmultifiles" multiple/>
<script>
function readmultifiles(e) {
  const files = e.currentTarget.files;
  Object.keys(files).forEach(i => {
    const file = files[i];
    const reader = new FileReader();
    reader.onload = (e) => {
      //server call for uploading or reading the files one-by-one
      //by using 'reader.result' or 'file'
    }
    reader.readAsBinaryString(file);
  })
};
</script>

【讨论】:

  • Array.from(files).foreach(file => {}) 也很好用
【解决方案4】:

我的完整解决方案在这里:

 <html> <body>
    <input type="file" id="filesx" name="filesx[]"
      onchange="readmultifiles(this.files)" multiple=""/>
    <div id="bag"></div>
<script>
window.onload = function() {
    if (typeof window.FileReader !== 'function') {
        alert("The file API isn't supported on this browser yet.");
    }
}

function readmultifiles(files) {
  var reader = new FileReader();  
  function readFile(index) {
    if( index >= files.length ) return;
    var file = files[index];
    reader.onload = function(e) {  
      // get file content  
      var bin = e.target.result;
      // do sth with bin
      readFile(index+1)
    }
    reader.readAsBinaryString(file);
  }
  readFile(0);

 function setup_reader(file) {
        var name = file.name;
        var reader = new FileReader();

            var ul = document.createElement("ul");
            document.getElementById('bag').appendChild(ul);
        reader.onload = function(e) {
            var bin = e.target.result; //get file content

            // do sth with text

            var li = document.createElement("li");
            li.innerHTML = name;
            ul.appendChild(li);
        }
        reader.readAsBinaryString(file);
    }

    for (var i = 0; i < files.length; i++) { setup_reader(files[i]); }
}
</script> </body> </html>

【讨论】:

  • 这个解决方案很完美!
【解决方案5】:

我使用现代 JS(地图、迭代器)实现了另一个解决方案。我从我的 Angular 应用程序中改编了代码(最初是用一些 TS 功能编写的)。

就像 Steve KACOU 提到的,我们为每个文件创建一个不同的 FileReader 实例。

<input type="file" id="filesx" name="filesx[]"
      onchange="processFileChange(this)" multiple=""/>
    function processFileChange(event) {
        if (event.target.files && event.target.files.length) {
            const fileMap = new Map();

            for (let i = 0; i < event.target.files.length; i++) {
                const file = event.target.files[i];
                const fileReader = new FileReader();
                fileMap.set(fileReader, file);
            }

            const mapEntries = fileMap.entries();
            readFile(mapEntries);
        }
    }

    function readFile(mapEntries) {
        const nextValue = mapEntries.next();

        if (nextValue.done === true) {
            return;
        }

        const [fileReader, file] = nextValue.value;

        fileReader.readAsDataURL(file);
        fileReader.onload = () => {
            // Do black magic for each file here (using fileReader.result)

            // Read the next file
            readFile(mapEntries);
        };
    }

基本上,这利用了通过引用传递对象的优势,以使每次迭代都使地图永久化。在我看来,这使得代码很容易阅读。

【讨论】:

    【解决方案6】:

    使用multiple 属性定义输入:

    <input onchange = 'upload(event)' type = 'file' multiple/>
    

    定义上传函数:

    const upload = async (event) => {
      
        // Convert the FileList into an array and iterate
        let files = Array.from(event.target.files).map(file => {
    
            // Define a new file reader
            let reader = new FileReader();
    
            // Create a new promise
            return new Promise(resolve => {
    
                // Resolve the promise after reading file
                reader.onload = () => resolve(reader.result);
    
                // Read the file as a text
                reader.readAsText(file);
    
            });
    
        });
    
        // At this point you'll have an array of results
        let res = await Promise.all(files);
      
    }
    

    【讨论】:

      【解决方案7】:

      您必须为每个文件实例化一个 FileReader 才能读取。

      function readFiles(event) {
        //Get the files
        var files = event.input.files || [];
        if (files.length) {
          for (let index = 0; index < files.length; index++) {
            //instantiate a FileReader for the current file to read
            var reader = new FileReader();
            reader.onload = function() {
              var result = reader.result;
              console.log(result); //File data
            };
            reader.readAsDataURL(files[index]);
          }
        }
      }
      
      

      【讨论】:

        【解决方案8】:

        从这些答案中提取最好的部分。

        <input type="file" onchange="readmultifiles(this.files)" multiple />
        <script>
        function readmultifiles(files) {
          for (file of files) {
            const reader = new FileReader();
            reader.readAsBinaryString(file);
            reader.fileName = file.name;
            reader.onload = (event) => {
              const fileName = event.target.fileName;
              const content = event.currentTarget.result;
              console.log({ fileName, content });
            };
          }
        }
        
        </script>
        

        【讨论】:

        • 据我所知,这不会解决他希望文件并行读取的问题。
        【解决方案9】:

        试试这个

        const setFileMultiple = (e) => {
          e.preventDefault();
          //Get the files
          let file = [...e.target.files] || [];
        
          file.forEach((item, index) => {
            let reader = new FileReader();
        
            reader.onloadend = () => {
              console.log("result", reader.result);
            };
            reader.readAsDataURL(file[index]);
          });
        
        };
        

        【讨论】:

        • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-25
        • 2014-01-18
        • 1970-01-01
        • 2021-09-29
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多