客户端实现阿里云OSS文件上传(分片上传,断点续传)

前言

阿里云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'
  })
);

分片上传

分片上传的步骤:

  1. 初始化分片上传:调用OSS API初始化一个分片上传会话,获取一个Upload ID。Upload ID用于标识这次分片上传操作。
  2. 分片切割:将待上传的大文件切割成固定大小的分片。
  3. 逐个上传分片:按顺序将每个分片逐个上传到阿里云OSS,每个分片上传成功后会返回一个ETag,用于标识该分片。
  4. 完成分片上传:在所有分片都上传完成后,调用OSS API完成分片上传操作。在此步骤中,需要将每个分片的ETag和分片号按顺序传递给OSS,OSS将根据这些信息进行分片合并,形成完整的大文件。

代码实现:

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

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
阿里云OSS分片文件上传是一种将大文件分成多个数据块(Part)进行上传的方法,以实现高效的文件上传断点续传功能。这种分片上传的方法可以应用于需要传输大文件的场景,比如视频点播文件等。 分片上传的步骤包括初始化一个分片上传事件、上传分片和完成分片上传。首先,需要初始化一个分片上传事件,获取一个唯一的上传ID和用于标识上传的文件名。然后,将文件分成多个数据块,逐个上传每个数据块,并记录每个数据块的编号和ETag。最后,通过调用完成分片上传的接口,将所有上传的数据块合并成一个完整的文件。 在分片上传过程中,可以通过监控上传进度,实时获取上传的数据块数目和总数据块数,从而了解上传的进度情况。可以使用两种方式来实现监控上传进度,一种是通过查询上传事件的接口来获取已经上传的数据块数目和总数据块数,另一种是通过每个数据块上传完成时的回调来更新上传进度。 在使用分片上传时,需要注意一些事项。首先,每个数据块的大小可以根据实际需求进行调整,但要遵循OSS的限制条件。其次,上传数据块时需要保证数据块的顺序和完整性,以确保数据块可以正确地组合成完整的文件。最后,完成分片上传后,需要注意保存上传ID和ETag,以便之后可以进行断点续传操作。 总结来说,阿里云OSS分片文件上传是一种将大文件分成多个数据块进行上传的方法,可以实现高效的文件上传断点续传功能。通过分片上传,可以有效地应对大文件传输的需求,并且通过监控上传进度和注意事项,可以更好地控制和管理文件上传的过程。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [阿里云对象存储OSS-分片上传](https://blog.csdn.net/Morris_/article/details/102813989)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值