指点成金-最美分享吧

登录

阿里云OSS文件上传(分片上传断点续传)前后端实现

佚名 举报

篇首语:本文由小编为大家整理,主要介绍了阿里云OSS文件上传(分片上传断点续传)前后端实现相关的知识,希望对你有一定的参考价值。

转载地址:https://segmentfault.com/a/1190000020963346        

 

关于阿里云 OSS 的介绍请参考官方文档:阿里云 OSS。

出于账号安全的考虑,前端使用 OSS 服务需要走临时授权,即拿一个临时凭证(STS Token)去调用 aliyun-oss SDK。关于临时授权请参考:RAM 和 STS 介绍,RAM 子账号,STS 临时授权访问 OSS。

以 NodeJs 为例,后端给前端颁发临时凭证的实现可参考:Node STS 授权访问

前端上传文件到阿里云的相关操作可参考:浏览器端上传文件

了解以上概念之后,接下来可以去阿里云 OSS 的控制台进行相关的设置了(前提是开通了 OSS 服务)。

阿里云 OSS 控制台配置

1. 创建 Bucket

首先,我们创建一个 bucket,一个存储文件的容器:

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

2. 创建 RAM 用户

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

首先,我们创建一个用户,并给该用户分配调用 STS 服务 AssumeRole 接口的权限,这样待会儿后端就能以该用户的身份给前端分配 STS 凭证了:

我们需要保存一下该用户的 access key 和 access key secret,后端需要以此核实用户的身份。

3. 创建 RAM 角色

该角色即有权限在前端调用 aliyun-oss SDK 上传文件的用户角色,例如我们创建一个只有上传权限的角色,命名为 uploader:

接下来我们需要给该角色分配权限,可以通过创建一条权限策略并分配给角色,该权限策略里面只包含了上传文件、分片上传相关的权限:

策略具体内容为:

  "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/*"      ]      ]

然后,把该策略赋予 uploader 角色:

到此,阿里云 OSS 后台相关配置结束。接下来,我们来关注前后端的实现。

后端实现

由于是前端负责上传,所以后端的任务比较简单,就是提供一个 STS Token 给前端。本文以 NodeJs 为例实现如下。

1. 安装 aliyun-oss SDK

npm install ali-oss

2. 生成 STS Token 并返回

const OSS = require("ali-oss");const STS = OSS.STS;const sts = new STS(  accessKeyId: process.env.ALIYUN_OSS_RULE_ASSUMER_ACCESS_KEY,  accessKeySecret: process.env.ALIYUN_OSS_RULE_ASSUMER_ACCESS_KEY_SECRET);async function getCredential(req, res, next)   try     const  credentials  = await sts.assumeRole(      "acs:ram::1582938330607257:role/uploader",  // role arn      null, // policy      15 * 60, // expiration      "web-client" // session name    );    req.result = credentials;    next();   catch (err)     next(err);  

其中,access key 和 access key secret 保存在.env文件中。sts.assumeRole()返回的即为 STS Token,方法接收的四个参数分别为:role arnpolicyexpirationsession name

Role arn 可以从 RAM 角色的详情页面获取:

Policy 是自定义的策略,由于已经为角色添加了权限策略,所以可以传null

Expiration 是 STS Token 的过期时间,应该在 15min ~ 60min 之间。当 Token 失效时前端需要重新获取。

Session name 为自定义的一个会话名称。

后端实现完成!

前端实现

本文前端实现使用原生 JS,另外还有 ant design pro 的版本请参考 github 项目。

前端实现有几个关键点:

  1. 调用 aliyun-oss SDK 之前获取 STS Token
  2. 定义上传分片大小,如果文件小于分片大小则使用普通上传,否则使用分片上传
  3. 上传过程中能展示上传进度
  4. 上传过程中,如果 STS Token 快过期了,则先暂停上传重新获取 Token,接着进行断点续传
  5. 支持手动暂停、续传功能
  6. 上传完成后返回文件对应的下载地址

1. 引入 aliyun-oss SDK

参考 引入 aliyun-oss SDK

2. html

HTML 中包含文件选择器,上传、暂停、续传按钮,状态显示:

3. 定义变量

let credentials = null; // STS凭证let ossClient = null; // oss客户端实例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 = ; // 所有分片上传文件的检查点

4. 获取 STS 凭证,创建 OSS Client

// 获取STS Tokenfunction getCredential()   return fetch("http://localhost:5050/api/upload/credential")    .then(res =>       return res.json()    )    .then(res =>       credentials = res.result;    )    .catch(err =>       console.error(err);    );// 创建OSS Clientasync function initOSSClient()   const  AccessKeyId, AccessKeySecret, SecurityToken  = credentials;  ossClient = new OSS(    accessKeyId: AccessKeyId,    accessKeySecret: AccessKeySecret,    stsToken: SecurityToken,    bucket,    region  );

5. 点击上传按钮事件

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

6. 普通上传

// 普通上传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);  );

7. 分片上传

// 分片上传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);  );

9. 断点续传

// 断点续传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);    );  );

10. 分片上传进度

在 progress 回调中我们可以判断 STS Token 是否快过期了,如果快过期了则先取消上传获取新 Token 后在从之前的断点开始续传。

// 分片上传进度改变回调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();  

11. 暂停、续传按钮点击事件

// 暂停上传function stop()   status.innerText = "Stopping";  if (ossClient) ossClient.cancel();// 续传function resume()   status.innerText = "Resuming";  if (ossClient) resumeMultipartUpload();

github 示例项目

项目地址:https://github.com/MudOnTire/...。如果对大家有帮助,star一下吧。

以上是关于阿里云OSS文件上传(分片上传断点续传)前后端实现的主要内容,如果未能解决你的问题,请参考以下文章