断点续传在前端开发中通常用于处理大文件上传的情况,以减少网络波动对上传过程的影响,并提高用户体验。
步骤:
- 创建 Vue 项目: 使用 Vue CLI 创建一个新项目。
- 安装 Axios: 如果你选择使用 Axios 而不是原生的
fetch
。 - 编写组件: 创建一个包含文件输入框、上传按钮和进度条的 Vue 组件。
- 编写逻辑: 处理文件读取、分块、发送请求以及进度更新。
所需依赖:前提需搭建好vue项目
npm install --save axios
代码示例
<template>
<div>
<input type="file" @change="handleFileChange" ref="fileInput" />
<button @click="startUpload">开始上传</button>
<button @click="pauseUpload">暂停上传</button>
<button @click="resumeUpload">继续上传</button>
<progress :value="progress" max="100"></progress>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
file: null,
fileReader: null,
blobURL: '',
xhr: null,
progress: 0,
chunkSize: 1 * 1024 * 1024, // 1MB
currentChunk: 0,
isPaused: false,
lastUploadedByte: 0
};
},
methods: {
handleFileChange(event) {
this.file = event.target.files[0];
this.fileReader = new FileReader();
this.blobURL = URL.createObjectURL(this.file);
this.fileReader.onload = () => {
this.lastUploadedByte = this.getLastUploadedByte(); // Load from storage
};
this.fileReader.readAsArrayBuffer(this.file);
},
startUpload() {
if (!this.file) return;
this.isPaused = false;
this.uploadNextChunk();
},
pauseUpload() {
if (this.xhr) {
this.xhr.abort();
this.isPaused = true;
}
},
resumeUpload() {
this.startUpload();
},
uploadNextChunk() {
if (this.currentChunk * this.chunkSize > this.file.size) {
console.log('Upload completed!');
return;
}
const start = this.currentChunk * this.chunkSize;
const end = Math.min(start + this.chunkSize, this.file.size);
const blob = this.file.slice(start, end);
const formData = new FormData();
formData.append('file', blob, this.file.name);
formData.append('start', start.toString());
formData.append('end', end.toString());
this.xhr = axios.post('/api/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
'Last-Uploaded-Byte': this.lastUploadedByte.toString()
},
onUploadProgress: progressEvent => {
this.progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
}
}).then(response => {
this.currentChunk++;
this.lastUploadedByte = end;
this.storeLastUploadedByte(this.lastUploadedByte);
if (!this.isPaused) {
this.uploadNextChunk();
}
}).catch(error => {
console.error('Error uploading:', error);
});
},
getLastUploadedByte() {
return parseInt(localStorage.getItem('lastUploadedByte')) || 0;
},
storeLastUploadedByte(byte) {
localStorage.setItem('lastUploadedByte', byte);
}
}
};
</script>
后端接口:
你需要确保你的后端服务器支持断点续传。这通常涉及到检查请求头中的 Last-Uploaded-Byte
字段,并根据这个字段返回已上传的字节数。
以上代码展示了一个基本的 Vue.js 组件,它可以处理文件选择、文件分块并上传每个分块到服务器。此外,它还支持暂停和恢复上传操作,并记录已上传的字节数以便下次上传时可以从上次中断的地方继续。
详细解释:
Vue 组件结构:
- HTML: 包含一个文件输入框、三个按钮和一个进度条。
- Vue 数据模型: 定义了用于存储文件数据、上传状态等的变量。
- 方法:
handleFileChange
: 当用户选择文件时触发,读取文件内容,并加载上次上传的位置。startUpload
: 开始上传文件,从第一块开始。pauseUpload
: 暂停上传过程,取消当前的 XMLHttpRequest 请求。resumeUpload
: 重新开始上传过程。uploadNextChunk
: 上传文件的下一块,并递归调用自身直到完成所有块。getLastUploadedByte
: 从本地存储加载最后上传的字节数。storeLastUploadedByte
: 将最后上传的字节数保存到本地存储。
Vue 逻辑详解:
-
文件选择:
- 当用户通过
<input type="file">
选择文件后,handleFileChange
方法被触发。 - 文件被读取到内存中,同时从本地存储中加载最后上传的字节数。
- 当用户通过
-
开始上传:
- 用户点击“开始上传”按钮后,
startUpload
方法被调用。 - 方法首先检查是否有文件被选择,然后初始化上传过程,设置
isPaused
为false
并调用uploadNextChunk
。
- 用户点击“开始上传”按钮后,
-
暂停与继续:
- “暂停上传”按钮调用
pauseUpload
方法,如果当前有上传请求进行,则取消该请求。 - “继续上传”按钮调用
resumeUpload
方法,重新开始上传过程。
- “暂停上传”按钮调用
-
分块上传:
uploadNextChunk
方法负责将文件分割成块并上传每一块。- 它计算出当前块的起始位置和结束位置,并创建一个
blob
对象来代表这一块。 - 使用
FormData
将blob
和元数据(起始位置和结束位置)添加到请求中。 - 发送请求给服务器,并监听上传进度。
-
进度条更新:
- 在每次上传请求中,都监听
onUploadProgress
事件,根据已上传的数据量更新进度条的值。
- 在每次上传请求中,都监听
-
断点续传:
- 每次成功上传一个块后,都会更新
currentChunk
和lastUploadedByte
。 - 通过
storeLastUploadedByte
方法将lastUploadedByte
存储到浏览器的localStorage
中,以便在页面刷新或关闭后仍能恢复上传。 - 通过
getLastUploadedByte
方法从localStorage
中加载lastUploadedByte
。
- 每次成功上传一个块后,都会更新
后端接口:
你需要确保你的后端服务器支持断点续传。这通常涉及到检查请求头中的 Last-Uploaded-Byte
字段,并根据这个字段返回已上传的字节数。
注意事项:
- 错误处理: 以上示例代码中没有包括详细的错误处理逻辑。在实际应用中,你可能需要增加更多的错误捕获和重试机制。
- 性能优化: 对于非常大的文件,可能还需要考虑其他优化策略,比如并发上传多个块等。
- 安全性: 在真实的生产环境中,还需要考虑文件的验证、权限控制等问题。