在实际开发中,上传大文件往往会面临一些问题,如上传速度慢、超时、网络中断等。为了更好地解决这些问题,我们可以使用分片上传技术。分片上传就是将文件拆分成多个小块进行上传,每个小块称为"分片",上传成功后,服务器会将这些分片重新合并成完整的文件。
本文将通过一个基于 Vue 和 iView 的分片上传示例,详细介绍如何实现文件分片上传,并且为每个上传的分片提供上传进度显示和错误处理。
使用分片上传技术的优势:
- 提高上传成功率:即使某个分片上传失败,只需要重新上传该分片,其他分片不受影响。
- 支持断点续传:分片上传可以帮助我们实现断点续传功能。
- 支持上传大文件:通过将大文件切分成小的分片,避免一次性上传造成的超时问题。
前提条件
- Vue.js:用于构建用户界面。
- iView :一款基于 Vue 的 UI 组件库,其中包括用于文件上传的
Upload
组件。 - Axios:用于发起 HTTP 请求,进行文件上传。
- 后端 API:需要提供分片上传和文件合并接口。
1. 组件结构
首先,使用 iView 提供的 Upload
组件来构建文件上传界面,界面中包含一个选择文件按钮和一个上传进度条。在 Vue 的 data
中定义上传所需的一些变量,例如文件分片大小、上传进度、分片的集合等。
html
<template>
<div>
<!-- iView的Upload组件 -->
<Upload
:action="uploadAction"
:before-upload="beforeUpload"
:on-progress="handleProgress"
:on-success="handleSuccess"
:on-error="handleError"
:show-file-list="false"
:headers="uploadHeader"
>
<Button>选择文件</Button>
</Upload>
<div v-if="uploading">
<Progress :percent="uploadProgress"></Progress>
</div>
</div>
</template>
在上面的代码中,使用了 iView 的 Upload
组件来处理文件选择,Progress
组件用来展示文件上传的进度。before-upload
用于文件上传前的预处理,on-progress
用来更新进度条,on-success
和 on-error
用来处理上传成功或失败的回调。
2. 数据结构
javascript
data() {
return {
uploadHeader: {
Authorization: localStorage.token, // 上传请求的授权头
Accept: '*/*' // 允许所有类型的响应
},
fileName: '', // 存储文件名
fileSize: 0, // 存储文件大小
chunkSize: 5 * 1024 * 1024, // 每个分片的大小,这里设置为5MB
fileChunks: [], // 存储文件的所有分片
uploading: false, // 是否正在上传
uploadProgress: 0, // 上传进度
currentChunkIndex: 0, // 当前上传的分片索引
uploadAction: `${this.baseURL}/xxxx`, // 上传分片的API
uploadActionMerge: `${this.baseURL}/xxx`, // 合并分片的API
};
}
在 data
中,定义了上传的必要参数,包括文件名、文件大小、分片大小、上传进度等。uploadAction
和 uploadActionMerge
分别表示上传分片和合并文件的 API 地址。
3. 处理文件上传前的操作
当文件被选中后,触发 before-upload
回调函数,在该函数中我们进行文件的验证和分片处理。
javascript
// 上传前的验证和分片操作
beforeUpload (file) {
// 如果需要做格式验证,大小检查等,可以在这里添加验证逻辑
this.fileName = file.name; // 记录文件名称
this.fileSize = file.size; // 记录文件大小
// 调用分片处理方法
this.chunkFile(file);
return false; // 返回false表示不直接上传,手动上传
},
4. 分片处理
javascript
chunkFile(file) {
const totalChunks = Math.ceil(file.size / this.chunkSize); // 计算分片数量
this.fileChunks = []; // 清空分片数组
// 循环切割文件为多个分片
for (let i = 0; i < totalChunks; i++) {
const start = i * this.chunkSize;
const end = Math.min(start + this.chunkSize, file.size);
const chunk = file.slice(start, end);
this.fileChunks.push(chunk); // 将当前分片添加到分片数组
}
this.uploading = true; // 设置上传状态为进行中
this.uploadNextChunk(); // 上传第一个分片
}
在 chunkFile
方法中,我们通过计算文件的总大小和每个分片的大小来确定分片数量,并将文件切割成多个分片。然后,我们将每个分片存储在 fileChunks
数组中,并开始上传第一个分片。
5. 上传分片
上传每个分片时,我们使用 FormData
来构造上传的表单数据,并使用 axios
发送 HTTP 请求。
javascript
uploadChunk(chunk) {
const clientId = this.generateClientId(); // 生成唯一的客户端ID
const formData = new FormData();
formData.append('file', chunk); // 添加分片
formData.append('clientId', clientId); // 客户端ID
formData.append('chunkId', this.currentChunkIndex); // 当前分片索引
this.axios.post(this.uploadAction, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: (event) => {
const hasUploadTotal = this.currentChunkIndex * this.chunkSize;
const percent = Math.round((hasUploadTotal + event.loaded) * 100 / this.fileSize); // 计算上传进度
this.uploadProgress = percent; // 更新进度
},
}).then(res => {
if (res.data.code === 1) {
this.currentChunkIndex++; // 上传成功,增加分片索引
this.uploadNextChunk(clientId); // 上传下一个分片
} else {
this.$message.error('上传失败');
this.uploading = false;
}
}).catch(error => {
this.$message.error('上传失败');
this.uploading = false;
});
}
6. 合并文件
当所有分片上传完成后,我们需要调用后端接口来合并这些分片,最终生成完整的文件。
javascript
mergeFile(clientId, fileName) {
const url = `${this.uploadActionMerge}&clientId=${clientId}&sourceName=${fileName}`;
this.axios.post(url).then(({ data }) => {
if (data.code === 1) {
this.$message.success('上传成功');
} else {
this.$message.warning(data.message);
}
}).catch((err) => {
this.$message.error('合并文件出错');
}).finally(() => {
// 重置状态
this.resetUploadState();
});
}
7. 生成客户端ID
为了确保每次上传都能被唯一标识,我们使用当前时间戳和随机数生成一个客户端ID。
javascript
generateClientId() {
const timestamp = (+new Date()).toString(32); // 时间戳转为32进制
let randomStr = '';
// 随机生成字符
for (let i = 0; i < 5; i++) {
randomStr += Math.floor(Math.random() * 65535).toString(32);
}
return timestamp + randomStr; // 返回客户端ID
}
8. 上传进度
通过 onUploadProgress
回调函数,我们可以在文件上传过程中动态地更新进度条,实时展示上传进度。
javascript
handleProgress(event) {
const hasUploadTotal = this.currentChunkIndex * this.chunkSize;
const percent = Math.round((hasUploadTotal + event.loaded) * 100 / this.fileSize); // 计算总上传进度
this.uploadProgress = percent; // 更新进度条
}
9. 上传成功和失败处理
最后,我们还可以在上传成功或失败时,进行相应的处理。
javascript
handleSuccess(response) {
this.$message.success('上传成功');
this.uploading = false;
}
handleError(error) {
this.$message.error('上传失败');
this.uploading = false;
}
总结
通过以上步骤,成功实现了基于 Vue 和 iView 组件的分片上传功能。主要流程包括:文件选择、文件分片、逐个分片上传、上传进度显示以及文件合并等。该方案对于大文件上传具有较好的稳定性和效率,尤其适用于大文件的分片上传场景。