使⽤JS请你实现⼀个⼤⽂件上传和断点续传
⼤⽂件上传
整体思路
前端
前端⼤⽂件上传⽹上的⼤部分⽂章已经给出了解决⽅案,核⼼是 利⽤ Blob.prototype.slice ⽅法,和数组的 slice ⽅法相似,调⽤的 slice ⽅法可以返回 原⽂件的某个切⽚
这样我们就可以根据预先设置好的切⽚最⼤数量将⽂件切分为⼀个个切⽚,然后借助 http 的可并发性,同时上传多个切⽚,这样从原本传⼀个⼤⽂件,变成了 同时 传多个⼩的⽂件切⽚,可以⼤⼤减少上传时间
另外由于是并发,传输到服务端的顺序可能会发⽣变化,所以我们还需要给每个切⽚记录顺序
服务端
服务端需要负责接受这些切⽚,并在接收到所有切⽚后 合并 切⽚
这⾥⼜引伸出两个问题
1. 何时合并切⽚,即切⽚什么时候传输完成
2. 如何合并切⽚
第⼀个问题需要前端进⾏配合,前端在每个切⽚中都携带切⽚最⼤数量的信息,当服务端接受到这个数量的切⽚时⾃动合并,也可以额外发⼀个请求主动通知服务端进⾏切⽚的合并
第⼆个问题,具体如何合并切⽚呢?这⾥可以使⽤ nodejs 的 api fs.appendFileSync ,它可以同步地将数据追加到指定⽂件,也就是说,当服务端接受到所有切⽚后,先创建⼀个最终的⽂件,然后将所有切⽚逐步合并到这个⽂件中
talk is cheap,show me the code ,接着我们⽤代码实现上⾯的思路
前端部分
前端使⽤ Vue 作为开发框架,对界⾯没有太⼤要求,原⽣也可以,考虑到美观使⽤ element-ui 作为 UI 框架
上传控件
⾸先创建选择⽂件的控件,监听 change 事件以及上传按钮
<template>
<div>
<input type="file" @change="handleFileChange" />
<el-button @click="handleUpload">上传</el-button>
</div>
</template>
<script>
export default {
data: () => ({
container: {
file: null
}
}),
methods: {
handleFileChange(e) {
const [file] = e.target.files;
if (!file) return;
Object.assign(this.$data, this.$options.data());
},
async handleUpload() {}
}
};
</script>
请求逻辑
考虑到通⽤性,这⾥没有⽤第三⽅的请求库,⽽是⽤原⽣ XMLHttpRequest 做⼀层简单的封装来发请求
request({
url,
method = "post",
data,
headers = {},
requestList
}) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
Object.keys(headers).forEach(key =>
xhr.setRequestHeader(key, headers[key])
);
xhr.send(data);
resolve({
data: sponse
});
};
});
}
上传切⽚
接着实现⽐较重要的上传功能,上传需要做两件事对⽂件进⾏切⽚
将切⽚传输给服务端
<template>
<div>
<input type="file" @change="handleFileChange" /> <el-button @click="handleUpload">上传</el-button> </div>
</template>
<script>
+ const LENGTH = 10; // 切⽚数量
export default {
data: () => ({
container: {
file: null,
+ data: []
}
}),
methods: {
request() {},
handleFileChange() {},
+ // ⽣成⽂件切⽚
+ createFileChunk(file, length = LENGTH) {
+ const fileChunkList = [];
+ const chunkSize = il(file.size / length);
+ let cur = 0;
+ while (cur < file.size) {
+ fileChunkList.push({ file: file.slice(cur, cur + chunkSize) });
+ cur += chunkSize;
+ }
+ return fileChunkList;
+ },
+ // 上传切⽚
+ async uploadChunks() {
+ const requestList = this.data
+ .map(({ chunk }) => {
+ const formData = new FormData();
+ formData.append("chunk", chunk);
+ formData.append("hash", hash);
+ formData.append("filename", ainer.file.name);
+ return { formData };
+ })
+ .map(async ({ formData }) =>
+ quest({
+ url: "localhost:3000",
+ data: formData
js合并两个数组+ })
+ );
+ await Promise.all(requestList); // 并发切⽚
+ },
+ async handleUpload() {
+ if (!ainer.file) return;
+ const fileChunkList = ainer.file);
+ this.data = fileChunkList.map(({ file },index) => ({
+ chunk: file,
+ hash: ainer.file.name + "-" + index // ⽂件名 + 数组下标
+ }));
+ await this.uploadChunks();
+ }
}
};
</script>
当点击上传按钮时,调⽤ createFileChunk 将⽂件切⽚,切⽚数量通过⼀个常量 Length 控制,这⾥设置为 10,即将⽂件分成 10 个切⽚上传
createFileChunk 内使⽤ while 循环和 slice ⽅法将切⽚放⼊ fileChunkList 数组中返回
在⽣成⽂件切⽚时,需要给每个切⽚⼀个标识作为 hash,这⾥暂时使⽤ ⽂件名 + 下标 ,这样后端可以知道当前切⽚是第⼏个切⽚,⽤于之后的合并切⽚
随后调⽤ uploadChunks 上传所有的⽂件切⽚,将⽂件切⽚,切⽚ hash,以及⽂件名放⼊ FormData 中,再调⽤上⼀步的 request 函数返回⼀个 proimise,最后调⽤ Promise.all 并发上传所有的切⽚
发送合并请求
这⾥使⽤整体思路中提到的第⼆种合并切⽚的⽅式,即前端主动通知服务端进⾏合并,所以前端还需要额外发请求,服务端接受到这个请求时主动合并切⽚
<template>
<div>
<input type="file" @change="handleFileChange" />
<el-button @click="handleUpload">上传</el-button>
</div>
</template>
<script>
export default {
data: () => ({
container: {
file: null
},

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。