使用JavaScript将上传的文件拆分为多个块

18

我正在寻找一种在浏览器前端将任何文本/数据文件拆分成多个文件并上传的方法。我的限制是每次上传40KB。因此,如果用户上传一个400KB的文件,它会在前端将该文件拆分为10个单独的块或10个单独的文件,然后再将其上传到服务器。

目前,我通过将此文件转换为base64格式的字符串,然后将这个字符串拆分成40KB,这样就可以得到10个单独的块。然后我会将每个块上传,并以chunk-1-of-10、chunk-2-of-10等文件名来命名。

在下载这些文件时,我只需将所有这些块连接起来,并将其从base64重新转换为其文件格式。

有更好的方法吗?是否有一个库可以处理所有这些而不必从头开始编写?我不确定使用base64的方式是否是最佳方法。

2个回答

25

不需要使用FileReader将内容读入内存

使用base64只会增加需要上传的大小,base64占用的空间比原始数据多约33%

使用Blob.slice获取块

blob切片(块)不会增加内存,它只会创建一个指向旧blob的新引用,带有更改的偏移量和新的大小以从哪里开始读取。

fetch发送数据时,它将直接从磁盘传输到网络,甚至不会触及主线程。

// simulate a file from a input
const file = new File(['a'.repeat(1000000)], 'test.txt')

const chunkSize = 40000
const url = 'https://httpbin.org/post'

for (let start = 0; start < file.size; start += chunkSize) {
  const chunk = file.slice(start, start + chunkSize + 1)
  const fd = new FormData()
  fd.set('data', chunk)
  
  await fetch(url, { method: 'post', body: fd }).then(res => res.text())
}

谢谢您提供的解决方案!对此,一个快速的补充是在slice()的第二个参数中添加“start”,以获取后续的块。const chunk = file.slice(start, start + chunkSize + 1)。 - Pramod Mali
我们如何输出服务器响应? - Parsa Yazdani
1
@ParsaYazdani console.log(await fetch(url, {method: 'post', body: fd}).then(res => res.text())) - Sahil
3
这对我们起了作用,不过我们必须进行一些小调整,将在块计算中的+1去掉:const chunk = file.slice(start, start + chunkSize) - Ralf Jahr
需要循环遍历每个块并进行多个HTTP请求吗?不能使用文件读取流完成吗? - Phantom007
显示剩余2条评论

4

您可以使用FileReader将文件读取为二进制数据,而无需进行base64编码,然后将其发送:

const url = 'http://www.example.com/upload';

document.getElementById('file-uploader').addEventListener('change', function(e) {
    const size = 40000;
    var reader = new FileReader();
    var buf;
    var file = document.getElementById('file-uploader').files[0];
    reader.onload = function(e) {
        buf = new Uint8Array(e.target.result);
        for (var i = 0;  i < buf.length; i += size) {
            var fd = new FormData();
            fd.append('fname', [file.name, i+1, 'of', buf.length].join('-'));
            fd.append('data', new Blob([buf.subarray(i, i + size)]));
            var oReq = new XMLHttpRequest();
            oReq.open("POST", url, true);
            oReq.onload = function (oEvent) {
               // Uploaded.
            };
            oReq.send(fd);
        }
    }
      
    reader.readAsArrayBuffer(file);
});
<input type="file" id="file-uploader"/>


我们如何输出服务器响应? - Parsa Yazdani
需要遍历每个数据块并发出多个 HTTP 请求吗?不能使用文件读取流来完成吗? - Phantom007

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接