使用 Javascript FileReader API 一次读取多个文件
Posted
技术标签:
【中文标题】使用 Javascript FileReader API 一次读取多个文件【英文标题】:Reading multiple files with Javascript FileReader API one at a time 【发布时间】: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>
问题是同时读取所有文件,当文件的总大小(总和)非常大时,浏览器会崩溃。
我想一个接一个地读取文件,这样可以减少内存消耗。
这可能吗?
【问题讨论】:
刚刚实施了这个解决方案,会推荐这个解决方案 - ***.com/a/13975129/984471 【参考方案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。以上是关于使用 Javascript FileReader API 一次读取多个文件的主要内容,如果未能解决你的问题,请参考以下文章
使用 JavaScript 的 FileReader 接口检测文件的内容类型
使用FileReader和Promise时JavaScript内存泄漏
使用原生 Chrome Javascript/FileReader/DataView 读取 id3 v2.4 标签
使用FileReader在Javascript中同步读取多个文件