商会小程序附件上传:分片上传 + OSS 直传完整实现(Vue3 + 阿里云 OSS 实战)

商会小程序里有个高频需求:会员上传营业执照、资质证书、合同扫描件。这些文件有几个特点------单个可能几十 MB,弱网环境下容易传一半失败,而且会员不会耐心等。

一开始我们走的是"前端传应用服务器、服务器再转发 OSS"的两段式。问题很快出来了:大文件经过应用服务器,占用带宽和内存,并发一高服务器就扛不住;网络抖动时整个文件重传,会员等到失去耐心。

后来改成"前端直传 OSS + 大文件分片"。这篇把实现记下来。

先说边界

我们的场景:文件集中在图片和 PDF,单文件几 MB 到几十 MB,弱网(会员多在手机流量下操作)。基于这两点,我们排除了"直接整文件直传"------几十 MB 在 4G 下传一次失败就得从头来。也排除了"应用服务器中转"------前面说了带宽问题。

最终方案:大文件(超过 5 MB)走分片上传,小文件直传;直传和分片都走 OSS 临时凭证,不经过应用服务器。

后端:签发直传凭证

前端直传 OSS 不能用主账号 AK,要靠 STS 临时凭证,权限收敛到指定目录。

```javascript

// 服务端:签发 OSS 直传 STS 凭证(Node + ali-oss sts)

const STS = require('ali-oss').STS;

const sts = new STS({

accessKeyId: process.env.OSS_AK,

accessKeySecret: process.env.OSS_SK

});

async function getOssToken(dir, userId) {

// 把 userId 拼进上传路径,天然隔离不同会员的文件

const prefix = `{dir}/{userId}/`;

const res = await sts.assumeRole(

process.env.OSS_ROLE_ARN,

{

Version: '1',

Statement: [{

Effect: 'Allow',

Action: 'oss:PutObject',

Resource: \`acs:oss:\*:\*:${process.env.OSS_BUCKET}/${prefix}\*\`

}]

},

1800 // 凭证有效期 30 分钟

);

return {

region: process.env.OSS_REGION,

bucket: process.env.OSS_BUCKET,

prefix,

accessKeyId: res.credentials.AccessKeyId,

accessKeySecret: res.credentials.AccessKeySecret,

stsToken: res.credentials.SecurityToken

};

}

```

前端:分片上传

5 MB 以内的文件,直接用 `multipartUpload` 直传。超过的,手动切片,每片带序号上传,失败只重传当前片。

```typescript

import OSS from 'ali-oss';

import SparkMD5 from 'spark-md5';

const CHUNK = 5 * 1024 * 1024; // 5MB 一片

function sliceFile(file: File): Blob\[\] {

const chunks: Blob\[\] = \[\];

for (let i = 0; i < file.size; i += CHUNK) {

chunks.push(file.slice(i, i + CHUNK));

}

return chunks;

}

async function uploadWithRetry(

client: OSS,

key: string,

chunk: Blob,

index: number

) {

for (let attempt = 0; attempt < 3; attempt++) {

try {

await client.put(`{key}.part{index}`, chunk);

return;

} catch (e) {

if (attempt === 2) throw e;

await new Promise((r) => setTimeout(r, 800 * (attempt + 1)));

}

}

}

async function uploadFile(file: File, token: any) {

const client = new OSS({

region: token.region,

accessKeyId: token.accessKeyId,

accessKeySecret: token.accessKeySecret,

stsToken: token.stsToken,

bucket: token.bucket

});

const ext = file.name.split('.').pop();

const hash = await fileMd5(file); // 用于断点续传去重

const key = `{token.prefix}{hash}.${ext}`;

if (file.size <= CHUNK) {

await client.put(key, file);

return key;

}

const chunks = sliceFile(file);

// 每片独立上传,失败只重传该片

await Promise.all(

chunks.map((c, i) => uploadWithRetry(client, key, c, i))

);

// 合并分片(OSS 需先 initMultipartUpload,逐 part 上传后 complete)

return key;

}

function fileMd5(file: File): Promise<string> {

return new Promise((resolve) => {

const reader = new FileReader();

const spark = new SparkMD5.ArrayBuffer();

reader.onload = (e) => {

spark.append(e.target!.result as ArrayBuffer);

resolve(spark.end());

};

reader.readAsArrayBuffer(file);

});

}

```

踩过的坑

第一个是断点续传。文件传到第 7 片断了,刷新页面再传,前面 6 片白传。解决办法是用文件 MD5 当 key 前缀,上传前先查 OSS 已有哪些分片(`listParts`),只补传缺失的。这个逻辑加上后,弱网下的体验好了很多。

第二个是凭证过期。直传凭证 30 分钟有效,传大文件可能超时。做法是上传前检查凭证剩余时间,不足 5 分钟就重新签发,不中断当前传。

第三个是并发数。几十片一起 `Promise.all` 上传,手机浏览器会限流。我们限到 3 片并发,既快又稳。

第四个是文件类型。上传前不只看扩展名,要读文件头判断真实类型,避免会员把 exe 改成 pdf 传上来。校验不过直接拦在客户端。

第五个是进度反馈。分片上传要实时显示总进度,不能只显示"上传中"。每片完成更新一次进度条,会员才愿意等。

什么时候不需要这套方案

如果文件都很小(全是几 MB 内的图片),整文件直传 OSS 就够了,不用分片。如果根本不需要前端直传(比如只在管理后台由干事上传),走应用服务器中转也行,逻辑简单。分片直传的价值在于"大文件 + 弱网 + 高并发"三个条件同时成立。

我们这边在用的商会管理系统叫未来漫城·商会互联平台,上面会员资料上传现在走的就是分片直传 OSS,一个几十 MB 的资质包在手机流量下也能一次传完,断了还能续。

相关推荐
andrsted32 分钟前
练题簿在线免费刷题:个人备考,从练新题到模拟自测怎么用?
学习·微信小程序·小程序·学习方法·刷题
翼龙云_cloud12 小时前
亚马逊云服务器服务代理商:Bedrock 实践经验 Serverless 架构下的 AI 自动化落地
服务器·架构·serverless·云计算·aws
GPU实战笔记13 小时前
实例节点选择:创建入口与运行时稳定性的边界
云计算·a100·gpu 实例·节点选择·工作流验证
码间留白13 小时前
手把手把 GitHub 推上阿里云 ECS:Actions CI/CD 全链路避坑实录(折腾好几天,到 2 分钟见绿)
运维·阿里云·ci/cd·docker·devops·github actions
一木 之林17 小时前
微信小程序开发全攻略·文章版|05 路由·交互·网络与音乐播放器实战
网络·微信小程序·小程序·交互
niucloud-admin19 小时前
NIUCLOUD LITE AI开发文档—模块开发:微信小程序
微信小程序·小程序·notepad++
mengge.cloud19 小时前
1019Namespace ,Contexts ,Kubernetes Pod
运维·服务器·网络·云原生·容器·kubernetes·云计算
云老大-阿里云国际站代理商1 天前
网站遇到恶意爬虫和CC攻击,阿里云国际站WAF与Bot管理怎么搭配
爬虫·阿里云·云计算
weishuangyun11 天前
家政上门预约小程序怎么做?本地家政接单系统搭建指南
小程序