前置知识
AWS SDK浅析
AWS SDK是Amazon Web Services开发工具包的缩写,是一套面向开发人员的API库,可用于创建、配置和管理AWS服务;其面向的语言有多种,如JavaScript、Java、GO、PHP等,且可以和多个AWS服务进行交互,如Amazon S3、Amazon EC2等,SDK提供了丰富的功能,如API操作、命令行工具、计算实例、持久化存储等,本文主要介绍持久化存储的相关逻辑;
- AWS针对许多流行技术和编程语言都提供了对应的SDK,借助这些SDK可以轻松地使用相应的语言或技术调用AWS服务;当然AWS还针对AWS SaaS产品提供了SDK,因此您可以在代码中更高效的使用它们
- AWS SDK的使用场景
- 数据处理
- 使用AWS SDK操作Amazon S3,进行文件的下载,上传、拷贝等操作
- 计算实例
- 使用AWS SDK创建、启动、停止Amazon EC2实例,并与这些实例进行交互
- 消息服务
- 使用AWS SDK连接Amazon SNS、Amazon SQS等消息服务、发送和接收消息
- 安全认证
- 使用AWS SDK管理AWS资源访问权限
- 持久化存储
- 使用AWS SDK链接Amazon RDS、Amazon DynamonDB等持久化存储服务
- 其他高级功能
- 监控和调试
- AWS SDK可以和AWS CloudWatch、AWS X-Ray结合,实现应用程序的监控和调试
- 拓展性
- 可以调用Lambda函数
- 可以与Amazon EC2、Auto Scaling结合,实现自动拓展
- 场景适配
- AWS SDK支持多个平台、包括移动端(如iOS、Android)和桌面应用程序
- 监控和调试
- 数据处理
- AWS SDK的性能优化相关
- 控制请求频率:控制请求时间间隔,防止请求过于频繁
- 使用缓存:来减少对AWS服务的请求
- 使用批处理:使用AWS SDK的批处理功能,可以一次性处理多个请求,减少请求次数
- 使用多线程:可以加速AWS SDK的执行速度,当处理的任务是CPU密集型的,则需要考虑使用多核CPU
定向分析AWS SDK
AWS SDK for JavaScript
针对JavaScript版本的AWS SDK主要有两版本aws-sdk - JavaScript1和@aws-sdk/client-s3 - javascriptv3,一般采用版本一,版本三对于node和项目webpack构建有要求,旧项目会有各种兼容性的问题;
分片上传在AWS SDK中的实践
分段上传允许上传单个对象作为一组分段。每个分段都是对象数据的连续部分。您可以独立上传以及按任意顺序上传这些对象分段。如果任意分段传输失败,可以重新传输该分段且不会影响其他分段。上传完所有的数据元分段后,Amazon S3 将汇集这些分段并创建数据元
具体步骤分析
分段上传主要分为三步骤:开始上传、上传对象分段、上传所有的分段后的完成分段上传
- 依赖安装
npm install @aws-sdk/client-s3
npm install aws-sdk
- 推荐在旧项目中使用第二个,新版本会对项目的环境和依赖有要求,有可能会产生大量的兼容错误
- CDN方式直接引入
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.744.0.min.js"></script>
- 在NodeJS中使用AWS
js
// import entire SDK
import AWS from 'aws-sdk';
// import AWS object without services
import AWS from 'aws-sdk/global';
// import individual service
import S3 from 'aws-sdk/clients/s3';
-
将对应的SDK继承到项目中
- 新版本
jsconst { S3Client, CreateMultipartUploadCommand, } = require("@aws-sdk/client-s3")
const AWS = require('aws-sdk')
-
通过服务端获取到AWS SDK鉴权的相关字段数据
- 主要就是AK、SK、endpoint和bucket等数据
-
初始化SDK
js
async initS3Client(){
AWS.config.update({
// 配置全局对象
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html
// 必填项 指定用于确定服务和资源访问权限的凭证
credentials: {
accessKeyId: "accessKeyId",
secretAccessKey: "secretAccessKey",
},
region: "beijing",
maxRetries: 1, //重试次数控制
});
S3Bucket = new AWS.S3({
// 设置AWS区域
apiVersion: '2023-11-16',
// region: 'beijing', //需要和update中对应
params:{
Bucket: 'Lbxin001', // 上传到的存储桶名称。
},
endpoint: 'beijing.xstore.lbxin.com',
// endpoint存储文件的服务器的地址,无端口 有默认的端口,如需要转换接口,可以通过nginx,这里加了端口后会报错
});
// this.uploadMedia()
// this.CreateBucketCommand(S3Bucket)
// this.createMultipartUpload('Lbxin001','output.webm',S3Bucket,'application/octet-stream')
// S3Bucket.getObject({
// Bucket: 'Lbxin001',
// key: ''
// })
}
- 建立连接,获取到S3 bucket中的UploadId字段,后续长传逻辑会用到这个字段进行逻辑校验
js
/**
* 启动分段上传
* @aim:建立S3连接 每上传一个新文件时(分片时不需要,一次就好),都需要与服务器建立连接 并获取唯一的uploadId
* @param {存储桶名称} bucket
* @param {标识文件的名称} key
* @param {s3为创建bucket时创建的client} s3
* @param {文件的类型} type
*/
// 初始化分段上传
async createMultipartUpload(bucket, key, s3=S3Bucket, type) {// string, string
const params = {
Bucket: bucket,
Key: key,
ContentType: type
};
s3.createMultipartUpload(params,(err,{Bucket,key,UploadId}) => {
if(err) console.log(err,'err=========')
else {
const { awsBucketData } = this.state
this.setState({
awsBucketData:{
UploadId,
...awsBucketData
}
})
}
});
}
-
分片上传到bucket
js/** * 文件分片上传至bucket * @param {分片数据内容} data * @param {为建立连接时返回的唯一id} uploadId * @param {文件名} key * @param {存储桶名称} bucket * @param {第几个分片} num * @param {s3为创建bucket时创建的client} s3 */ async unloadPart(data,uploadId,key,bucket,num,s3){ const params = { Bucket: bucket, Key: key, PartNumber: num, UploadId: uploadId, Body: data } // this.axiosAws(data,num,uploadId)通过原生的方式进行AWS数据的上传,后续的服务端介入AWS后会通过该原生的方式进行数据的上传 console.log(data,uploadId,key,bucket,num,s3,'data,uploadId,key,bucket,num,s3=========') s3.uploadPart(params,(err,data) => { if(err) console.log(err,'上传分片错误信息=========') else { console.log(data,'上传分片成功信息=========') // 内部需要将data中的xml数据中的ETag进行存储 后续的合并bucket需要用到这个额ETag和对应的partNumber // <CompleteMultipartUploadResult> // <Location> // https: //lbxin-video-pro.oss-cn-beijing.aliyuncs.com/users/77864/isrK2311Umn.webm // </Location> // <Bucket> // lusun-svc-pro // </Bucket> // <Key> // users/77864/isrK2311Umn.webm // </Key> // <ETag> // "0DD73931869F0BE68211C89D3AA3FE06-6" // </ETag> // </CompleteMultipartUploadResult> } }); }
- 有时候会存在
uploadPart
返回值中没有ETag,此时可以检查S3中的CORS的rule中是否设置了允许暴露ETag🔗
- 有时候会存在
-
完成对应分片的合并操作
- 此处需要将上一步中记录的所有key和ETag数据以xml的形式发送到AWS上,以此为参考依据进行数据的合并操作
分片上传完成且合并处理完成后,系统会自动的断开处理,如发生异常,上传失败后需要手动的断开连接;
js
completeMultipartUpload(Bucket,key,Parts,uploadId){
S3Bucket.completeMultipartUpload({
Bucket,
key,
MultipartUpload: {
Parts
},
uploadId
},(err,data) => {
if(err) console.log(err,'分片合并失败=========')
else console.log(data,'分片合并成功=========')
})
}
const {awsBucketData:{UploadId}} = this.state
await this.completeMultipartUpload('xiaohumeng001','output.webm',Parts,UploadId)
AWS在前后端联调中的相关实践
- 主要逻辑
- 鉴权相关的由服务端和AWS进行交互,如任务创建、ak/sk的校验、获取分片上传的地址信息、结束上传的逻辑交互等
- 前端和服务端之间的交互只是请求到AWS的相关地址信息
- 数据交互方面由前端和AWS直接交互,如上传、暂停、重传、结束上传等,同时需要注意的是如有头信息缺失时需要进行更改头信息时是需要将第一片数据进行更改然后再进行重传第一片到AWS上,S3会进行重传后的覆盖操作
推荐文献
Vue使用AWS s3进行大文件的分片上传、断点续传、下载(将文件上传进度显示)
aws-sdk example
AWS v2-to-v3
AWS v3-CreateMultipartUploadCommand-doc