前言–创建部分 链接到标题

创建一个 bucket,一个存储文件的容器

image-20260314213300932

接着,我们需要给 bucket 设置跨域,这样我们才能在网页中调用 Aliyun OSS 服务器的接口:

image-20260314213528094

基本就完成了,有余力还可以研究一下RAM

定义先行 链接到标题

对象存储(OSS/S3)≠ 本地文件夹

它是把文件拆成 “对象”(包含数据 + 元信息),存在分布式集群里

  • 优势:抗造(多副本存储)、能装(PB 级容量)、省钱(按使用量付费)
  • 场景:图片 / 视频存储、日志备份、用户文件上传(后端 er 的日常刚需)

单文件直接上传流程图

image-20260315102808803

大文件怎么上传? 链接到标题

用户传个 2G 的视频,直接调用上面的代码?结果:网络一波动就断,重传又要从头来

因此就需要分片上传!!

image.png

分片:

// 10MB一片(10*1024*1024字节)
const chunkSize = 10 * 1024 * 1024;
const chunks = Math.ceil(file.size / chunkSize);// 向上取整
// 切第一片:file.slice(0, chunkSize)

上传分片(携带信息)

合并分片completeMultipartUpload

前端实现 链接到标题

前端大文件核心是利用 Blob.prototype.slice 方法,和数组的 slice 方法相似,文件的 slice 方法可以返回原文件的某个切片

预先定义好单个切片大小,将文件切分为一个个切片,然后借助 http 的可并发性,同时上传多个切片。

这样从原本传一个大文件,变成了并发传多个小的文件切片,可以大大减少上传时间

定义元素和变量 链接到标题

元素包括:

<div>
  <input type="file" id='fileInput' multiple='true'>
  <button id="uploadBtn" onclick="upload()">Upload</button>
  <button id="stopBtn" onclick="stop()">Stop</button>
  <button id="resumeBtn" onclick="resume()">resume</button>
  <h2 id='status'></h2>
</div>

定义变量:

let credentials = null; // STS凭证
let ossClient; // oss客户端实例,创建需要用到accesskeyId
const fileInput = document.getElementById('fileInput'); // 文件选择器
const status = document.getElementById('status'); // 状态显示元素
const bucket = 'mudontire-test'; // bucket名称
const region = 'oss-cn-shanghai'; // oss服务区域名称
const partSize = 1024 * 1024; // 每个分片大小(byte)
const parallel = 3; // 同时上传的分片数
const checkpoints = {}; // 所有分片上传文件的检查点
  ossClient = new OSS({
    accessKeyId: AccessKeyId,
    accessKeySecret: AccessKeySecret,
    stsToken: SecurityToken,
    bucket,
    region
  });

定义事件以及对应的逻辑 链接到标题

点击上传按钮 链接到标题

​ 更改状态–拿到文件–转为数组List–forEach遍历—判断是否小于分片大小{ 走普通上传逻辑、走分片上传逻辑}

async function upload() {
  status.innerText = 'Uploading';
  const { files } = fileInput;
  const fileList = Array.from(files);
  const uploadTasks = fileList.forEach(file => {
    // 如果文件大学小于分片大小,使用普通上传,否则使用分片上传
    if (file.size < partSize) {
      commonUpload(file);
    } else {
      multipartUpload(file);
    }
  });
}

普通上传事件 链接到标题

​ 拿到OSS客户端–拿到文件名–Client.put(fileName,file)方法执行上传操作,

​ 该方法返回一个 Promise 对象,因此可以用 .then/.catchasync/await 处理结果。成功的话,执行回调

// 普通上传
async function commonUpload(file) {
  if (!ossClient) {
    await initOSSClient();
  }
  const fileName = file.name;
  return ossClient.put(fileName, file).then(result => {
    console.log(`Common upload ${file.name} succeeded, result === `, result)
  }).catch(err => {
    console.log(`Common upload ${file.name} failed === `, err);
  });
}

分片上传 链接到标题

​ Client.multipartUpload(fileName,file,options ) ,其中这个options包括:

配置项 类型 说明
parallel number 并行上传的分片数(默认 5),如设为 3 表示同时上传 3 个分片
partSize number 每个分片的大小(单位:字节,默认 10MB),如 1024 * 1024 * 20 表示 20MB / 分片
progress function 上传进度回调函数,实时返回上传进度
checkpoint 断点信息,用于续传(从上次失败的分片开始)
timeout 每个分片上传的超时时间(单位:毫秒)

progress 回调函数返回的参数有:

参数 来源 含义
p SDK 自动计算并传入 上传进度,范围 0 ~ 1(0% ~ 100%)
checkpoint SDK 自动传入 断点续传的检查点信息
res SDK 自动传入 当前分片的响应信息
path 你传入的 上传的文件路径/名称

这个里面包含了如下的信息:

checkpoint = {
  uploadId: "0004B9894C1E4C4E8B7C8B9A12345678",  // ← OSS 生成的唯一 ID
  file: File,                                       // 文件对象
  name: "example.jpg",                              // 文件名
  partSize: 102400,                                 // 分片大小
  doneParts: [                                      // 已上传的分片信息
    { number: 1, etag: "\"ABC123\"" },
    { number: 2, etag: "\"DEF456\"" }
  ],
  // ... 其他信息
}
// 分片上传
async function multipartUpload(file) {
  if (!ossClient) {
    await initOSSClient();
  }
  const fileName = file.name;
  return ossClient.multipartUpload(fileName, file, {
    parallel,
    partSize,
    progress: onMultipartUploadProgress
  }).then(result => {
    // 生成文件下载地址
    const url = `http://${bucket}.${region}.aliyuncs.com/${fileName}`;
    console.log(`Multipart upload ${file.name} succeeded, url === `, url)
  }).catch(err => {
    console.log(`Multipart upload ${file.name} failed === `, err);
  });
}

进度回调函数 链接到标题

// 分片上传进度改变回调
async function onMultipartUploadProgress(progress, checkpoint) {
  console.log(`${checkpoint.file.name} 上传进度 ${progress}`);
  checkpoints[checkpoint.uploadId] = checkpoint;
  // 判断STS Token是否将要过期,过期则重新获取
  const { Expiration } = credentials;
  const timegap = 1;
  if (Expiration && moment(Expiration).subtract(timegap, 'minute').isBefore(moment())) {
    console.log(`STS token will expire in ${timegap} minutes,uploading will pause and resume after getting new STS token`);
    if (ossClient) {
      ossClient.cancel();
    }
    await getCredential();
    await resumeMultipartUpload();
  }
}

断点续传 链接到标题

resumeMultipartUpload 也是阿里云 OSS SDK 提供的方法,用于断点续传。可以用保存的 checkpoint 继续上传,不用从头开始

方法 所属 作用
resumeMultipartUpload OSS SDK 内置方法 从断点(checkpoint)继续未完成的断点续传

他需要根据保存的checkpoint信息进行续传,

// 断点续传
async function resumeMultipartUpload() {
  Object.values(checkpoints).forEach((checkpoint) => {
    const { uploadId, file, name } = checkpoint;
    ossClient.multipartUpload(uploadId, file, {
      parallel,
      partSize,
      progress: onMultipartUploadProgress,
      checkpoint
    }).then(result => {
      console.log('before delete checkpoints === ', checkpoints);
      delete checkpoints[checkpoint.uploadId];
      console.log('after delete checkpoints === ', checkpoints);
      const url = `http://${bucket}.${region}.aliyuncs.com/${name}`;
      console.log(`Resume multipart upload ${file.name} succeeded, url === `, url)
    }).catch(err => {
      console.log('Resume multipart upload failed === ', err);
    });
  });
}

暂停和续传按钮 链接到标题

// 暂停上传
function stop() {
  status.innerText = 'Stopping';
  if (ossClient) ossClient.cancel();
}

// 续传
function resume() {
  status.innerText = 'Resuming';
  if (ossClient) resumeMultipartUpload();
}

秒传是什么? 链接到标题

秒传的本质是:上传前先计算文件的唯一哈希(MD5/SHA1),先查 OSS 是否已有该哈希对应的文件,有则直接返回文件地址(无需上传),无则正常上传

文件哈希是唯一标识(相同文件哈希必然相同);OSS 支持通过 oss:GetObjectMeta 或自定义元信息(如 x-oss-meta-filehash)判断文件是否存在。

优化点 说明
MD5 计算优化 大文件只计算前 1MB + 文件大小,或分片采样
Web Worker MD5 计算放 Web Worker,避免阻塞主线程
本地缓存 已上传文件的 MD5 存 localStorage,下次直接秒传
OSS Callback 上传成功后 OSS 主动回调服务端记录,更可靠

组件参数的设置 链接到标题

用props传参,指定类型和默认值

定义一些布尔类型,保存按钮的状态和数量(上传、暂停、恢复)、是否分片

分片大小、并发数量、阿里云客户端、并发数等

OSS临时凭证 链接到标题

如果不想让其他人访问到自己OSS里面的资源,可以设置STStoken

前往 RAM 控制台进行子账号和权限的配置。

首先创建一个用户,并给该用户分配调用 STS 服务 AssumeRole 接口的权限,保存一下该用户的 access key 和 access key secret,创建 RAM 角色(即有权限在前端调用 aliyun-oss SDK 上传文件的用户角色)

创建 RAM

用户拿到凭证 accesskey

策略:

{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "oss:PutObject",
        "oss:InitiateMultipartUpload",
        "oss:UploadPart",
        "oss:UploadPartCopy",
        "oss:CompleteMultipartUpload",
        "oss:AbortMultipartUpload",
        "oss:ListMultipartUploads",
        "oss:ListParts"
      ],
      "Resource": [
        "acs:oss:*:*:mudontire-test",
        "acs:oss:*:*:mudontire-test/*"
      ]
    }
  ]
}
/**
 *  @description 获取临时凭证
 */
async getOss() {
  let res = await request({
    url: '/StsToken', // 获取oss临时凭证接口,根据自己配置修改
  })
  let isPass = {
    pass: true,
  }
  if (res.status === 200) {
    this.credentials = res.data
  } else {
    isPass = { ...res, pass: false }
  }
  return isPass
},