前言–创建部分 链接到标题
创建一个 bucket,一个存储文件的容器

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

基本就完成了,有余力还可以研究一下RAM
定义先行 链接到标题
对象存储(OSS/S3)≠ 本地文件夹
它是把文件拆成 “对象”(包含数据 + 元信息),存在分布式集群里
- 优势:抗造(多副本存储)、能装(PB 级容量)、省钱(按使用量付费)
- 场景:图片 / 视频存储、日志备份、用户文件上传(后端 er 的日常刚需)
单文件直接上传流程图

大文件怎么上传? 链接到标题
用户传个 2G 的视频,直接调用上面的代码?结果:网络一波动就断,重传又要从头来
因此就需要分片上传!!
分片:
// 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/.catch 或 async/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
},