Nodejs 前端基于FileReader的断点续传
Nodejs 前端基于FileReader的断点续传
准备用FileReader来做断点续传,但是目前遇到的问题是,大文件的时候,不能把文件全读入内存,就需要分段读取文件内容,前端JS也没有类似Stream的pipe方法,能够在一个请求中,将所有分段读取的内容全部给服务器,目前看到有人的解决方案是,每次读取10M内容,然后每读一次,就发送一次请求,直到文件上传成功,请问有办法能在一个请求中实现断点续传的功能么?
Node.js 前端基于FileReader的断点续传
在处理大文件上传时,一次性将整个文件读入内存是非常低效且可能引起内存溢出的问题。因此,我们需要将文件分段读取并逐段上传。尽管浏览器的FileReader
对象没有直接提供类似于Node.js中的流式处理(如pipe
方法),我们仍然可以通过分段读取文件内容,并通过多次请求将这些片段上传到服务器。
分段读取文件
首先,我们需要定义一个函数来读取文件的指定部分。我们可以使用FileReader
的readAsArrayBuffer
方法来读取文件的特定部分。为了实现这一点,我们可以创建一个自定义函数,该函数接受文件对象、开始位置和结束位置作为参数,并返回一个包含该范围内的文件数据的Promise
。
function readBlob(file, start, end) {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
const blob = file.slice(start, end); // 获取文件的特定部分
fileReader.onload = function(e) {
resolve(e.target.result);
};
fileReader.onerror = function() {
reject(new Error('Failed to read blob'));
};
fileReader.readAsArrayBuffer(blob);
});
}
断点续传功能
接下来,我们需要实现一个断点续传的功能。这通常涉及到检查已经上传的部分,确定还需要上传哪些部分。假设服务器会返回一个标识符,表示当前已经上传的最大偏移量,我们可以根据这个信息来决定从哪里开始读取文件。
async function uploadFile(file, chunkSize = 10 * 1024 * 1024, offset = 0) {
const totalSize = file.size;
while (offset < totalSize) {
const end = Math.min(offset + chunkSize, totalSize);
const data = await readBlob(file, offset, end);
// 发送HTTP请求将数据发送到服务器
await fetch('/upload', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream'
},
body: data,
});
// 更新偏移量
offset = end;
}
}
示例调用
最后,我们可以调用上述函数来上传文件。如果需要支持断点续传,可以将已上传的部分保存在本地存储(如localStorage
)中,并在下次上传时恢复该状态。
const fileInput = document.getElementById('file');
fileInput.addEventListener('change', async () => {
const file = fileInput.files[0];
let offset = localStorage.getItem('offset') || 0;
await uploadFile(file, 10 * 1024 * 1024, parseInt(offset));
localStorage.setItem('offset', file.size); // 上传完成后更新偏移量
});
通过这种方式,我们可以有效地实现断点续传功能,即使在网络不稳定的情况下也能保证文件的完整上传。
顶上去
我觉得这个有可能,你先请求一下服务器给你一个id,然后服务器记录这个文件传了多少,然后你从那个位置开始读取
对于基于 FileReader
的断点续传问题,确实如你所说,由于浏览器端没有直接支持流处理的 API(如 Node.js 中的 Stream
),我们需要通过多次发送小块数据来实现。但是,我们可以通过一些策略在一个请求中实现断点续传功能。
一种可行的方法是在客户端使用 FileReader
分段读取文件,并在每次读取后发送一段数据到服务器。服务器端需要提供相应的接口来处理这些分段数据,并能识别出哪些部分已经上传,哪些部分还需要重新上传或继续上传。
这里提供一个简单的示例,展示如何实现这样的功能:
客户端 JavaScript 示例
function uploadFileChunk(file, chunkSize, start) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
let end = start + chunkSize;
if (end > file.size) {
end = file.size;
}
const blob = file.slice(start, end);
reader.readAsArrayBuffer(blob);
reader.onload = function() {
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload-chunk', true);
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.setRequestHeader('X-File-Start', start);
xhr.send(reader.result);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
resolve(xhr.responseText);
} else if (xhr.readyState === 4) {
reject(xhr.statusText);
}
};
};
reader.onerror = function() {
reject(new Error('Failed to read file'));
};
});
}
// 调用函数开始上传
async function startUpload(file, chunkSize = 10 * 1024 * 1024) { // 10MB
let start = 0;
while (start < file.size) {
try {
await uploadFileChunk(file, chunkSize, start);
start += chunkSize;
} catch (error) {
console.error('Error:', error);
break;
}
}
}
服务器端 Node.js 示例(简化版)
const express = require('express');
const fs = require('fs');
const app = express();
app.post('/upload-chunk', (req, res) => {
const start = parseInt(req.headers['x-file-start'], 10);
const file = fs.createWriteStream(`./uploads/${Date.now()}-${start}-${start + req.headers['content-length']}`, { flags: 'a' });
req.pipe(file);
req.on('end', () => {
res.send('Chunk uploaded successfully');
});
req.on('error', (err) => {
res.status(500).send('Error uploading chunk');
});
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
这段代码展示了如何使用 FileReader
和 XMLHttpRequest
在前端进行文件分段上传,同时服务器端通过 Express 接收并保存这些分段数据。为了实现断点续传,你需要在前端记录上传状态,并在上传失败时恢复到正确的起点继续上传。