阿里云OSS(Object Storage Service)是一种稳定、安全、高扩展性的云存储服务,它允许您以低成本、高可靠、高可用的方式存储和访问任意类型的数据。在实际应用中,文件上传是一个常见的功能需求。为了提高上传效率和文件完整性,我们可以使用分片上传和断点续传技术。
分片上传,断点上传使用场景:
通常在文件大于100 MB的情况下,建议采用分片上传的方法,通过断点续传和重试,提高上传成功率。如果在文件小于100 MB的情况下使用分片上传,且partSize设置不合理的情况下,可能会出现无法完整显示上传进度的情况。对于小于100 MB的文件,建议使用简单上传的方式。
简单上传参考:https://help.aliyun.com/zh/oss/developer-reference/simple-upload-8?spm=a2c4g.11186623.0.0.3f742a44LqLGSc
接下来介绍在vue项目中如何使用分片上传和断点续传。
1.安装阿里云OSS SDK
npm install ali-oss --save
2.创建OSS
import OSS from "ali-oss";
const client = ref(
new OSS({
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,yourRegion填写为oss-cn-hangzhou。
region: 'yourRegion',
// 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
accessKeyId: 'yourAccessKeyId',
accessKeySecret: 'yourAccessKeySecret',
// 填写Bucket名称。
bucket: 'examplebucket'
})
);
分片上传的步骤:
代码实现:
const uploadProgress = ref(0); //上传进度
const paused = ref(false); //是否暂停
const name = ref(""); //文件名
const uploadId = ref(); //上传id
const chunkArr: any = ref([]);//分片上传的结果数组
let chunks: any = [];//文件分片结果
const uploadFile1 = async (file: any) => {
chunkArr.value = [];
uploadProgress.value = 0;
//获取文件分片的结果数组
chunks =sliceFile(file);
const { name: fileName, type: mimeType } = file;
name.value = fileName;
//初始化分片上传,获取Upload ID
const result = await client.value.initMultipartUpload(fileName);
uploadId.value = result.uploadId;
//分片上传
uploadChunk(chunks[0], 1);
};
// 分片
const chunkSize = ref(1 * 1024 * 1024); //分片大小
const size = ref(0);//文件大小
const sliceFile = (file: any) => {
const fileSize = file.size;
size.value = file.size;
const chunks = Math.ceil(fileSize / chunkSize.value); // 计算分片总数
const allChunks = [];
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize.value;
const end = Math.min(start + chunkSize.value, fileSize);
const chunk = file.slice(start, end);
allChunks.push(chunk);
}
return allChunks;
};
//上传
async function uploadChunk(chunk: any, partNumber: number) {
//判断是否暂停上传
if (paused.value) {
paused.value = false;
return;
}
//上传分片
const part = await client.value.uploadPart(
name.value,
uploadId.value,
partNumber,
chunk,
0,
chunk.size
);
chunkArr.value.push({ number: partNumber, etag: part.etag });
// 获取进度
uploadProgress.value = Number(
((partNumber / chunks.length) * 100).toFixed(2)
);
if (partNumber < chunks.length) {
//分片上传成功
partNumber++;
uploadChunk(chunks[partNumber - 1], partNumber);
} else {
// 分片全部上传完毕
await client.value.completeMultipartUpload(
name.value,
uploadId.value,
chunkArr.value
);
}
}
官方文档分片上传
//暂停上传
const stop = () => {
paused.value = true;
};
//继续上传(断点续传)
const continued = () => {
client.value.listParts(name.value, uploadId.value).then((result: any) => {
//获取已经上传分片的信息
var parts = result.parts || [];
var nextPartNumber = parts.length + 1;
//继续上传未上传的分片
uploadChunk(chunks[nextPartNumber - 1], nextPartNumber);
});
};
<template>
<el-upload
v-model:file-list="fileList"
class="upload-demo"
:before-upload="beforeUpload"
>
<div class="upload">
<el-button type="primary">Click to upload</el-button>
<el-progress
:percentage="uploadProgress"
type="line"
style="margin-left: 10px; width: 350px"
>
</el-progress>
</div>
</el-upload>
<el-button type="primary" size="small" @click="stop">暂停</el-button>
<el-button type="primary" size="small" @click="continued">继续</el-button>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { ElMessage, ElMessageBox, progressProps } from "element-plus";
import OSS from "ali-oss";
import type { UploadProps, UploadUserFile } from "element-plus";
const client = ref(
new OSS({
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,yourRegion填写为oss-cn-hangzhou。
region: 'yourRegion',
// 从STS服务获取的临时访问密钥(AccessKey ID和AccessKey Secret)。
accessKeyId: 'yourAccessKeyId',
accessKeySecret: 'yourAccessKeySecret',
// 填写Bucket名称。
bucket: 'examplebucket'
})
);
const beforeUpload = async (file: File) => {
try {
await uploadFile(file);
} catch (error) {
console.error("Error uploading file:", error);
}
return false;
};
//分片上传
const fileList = ref<UploadUserFile[]>([]);
const uploadProgress = ref(0); //上传进度
const paused = ref(false); //是否暂停
const name = ref(""); //文件名
const uploadId = ref(); //上传id
const chunkArr: any = ref([]);//分片上传的结果数组
let chunks: any = [];//文件分片结果
const uploadFile = async (file: any) => {
chunkArr.value = [];
uploadProgress.value = 0;
//获取文件分片的结果数组
chunks =sliceFile(file);
const { name: fileName, type: mimeType } = file;
name.value = fileName;
//初始化分片上传,获取Upload ID
const result = await client.value.initMultipartUpload(fileName);
uploadId.value = result.uploadId;
//分片上传
uploadChunk(chunks[0], 1);
};
// 分片
const chunkSize = ref(5 * 1024 * 1024); //分片大小
const size = ref(0);//文件大小
const sliceFile = (file: any) => {
const fileSize = file.size;
size.value = file.size;
const chunks = Math.ceil(fileSize / chunkSize.value); // 计算分片总数
const allChunks = [];
for (let i = 0; i < chunks; i++) {
const start = i * chunkSize.value;
const end = Math.min(start + chunkSize.value, fileSize);
const chunk = file.slice(start, end);
allChunks.push(chunk);
}
return allChunks;
};
//上传
async function uploadChunk(chunk: any, partNumber: number) {
//判断是否暂停上传
if (paused.value) {
paused.value = false;
return;
}
//上传分片
const part = await client.value.uploadPart(
name.value,
uploadId.value,
partNumber,
chunk,
0,
chunk.size
);
chunkArr.value.push({ number: partNumber, etag: part.etag });
// 获取进度
uploadProgress.value = Number(
((partNumber / chunks.length) * 100).toFixed(2)
);
if (partNumber < chunks.length) {
//分片上传成功
partNumber++;
uploadChunk(chunks[partNumber - 1], partNumber);
} else {
// 分片全部上传完毕
await client.value.completeMultipartUpload(
name.value,
uploadId.value,
chunkArr.value
);
}
}
//暂停上传
const stop = () => {
paused.value = true;
};
//继续上传(断点续传)
const continued = () => {
client.value.listParts(name.value, uploadId.value).then((result: any) => {
//获取已经上传分片的信息
var parts = result.parts || [];
var nextPartNumber = parts.length + 1;
//继续上传未上传的分片
uploadChunk(chunks[nextPartNumber - 1], nextPartNumber);
});
};
</script>
<style lang="scss" scoped>
.upload {
display: flex;
}
</style>
问题一:跨域问题
https://blog.csdn.net/StruggleRookie/article/details/119417281
问题二:使用OSS分片上传功能上传文件时报“Please set the etag of expose-headers in OSS”错误
https://help.aliyun.com/zh/oss/the-please-set-the-etag-of-expose-headers-in-oss-error-message-is-returned-when-you-use-multipart-upload-to-upload-files