商会小程序附件上传:分片上传 + 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 的资质包在手机流量下也能一次传完,断了还能续。

相关推荐
优度网2 小时前
微信小程序主体变更公证书线上办理流程与业务交接风险规避方法浅析(2026)
小程序
m0_587383004 小时前
外卖CPS系统开发实战:从架构设计到运营落地全指南
java·spring·小程序·架构·需求分析
workflower4 小时前
人形机器人技术与产业基础
机器人·云计算·软件工程·需求分析·软件需求
论文复现现场7 小时前
单卡RTX 3090能训练,切到4卡却OOM:Accelerate多卡训练怎么排查?
人工智能·pytorch·云计算·gpu算力·多卡训练
T011561811 小时前
全栈项目实战手记|艺培场馆课时预约小程序 PC 管理端 Demo 完整开发成果展示
小程序
凡泰AI11 小时前
如何为政务 APP 搭建开放平台,实现多部门、第三方供应商标准化入驻与统一管理
android·大数据·小程序·uni-app·app·政务
FX1317887KP_11 小时前
AI陪练正在重塑KET PET FCE口语备考,传统外教课还有必要吗?
人工智能·小程序·ket口语·pet口语·fce备考·剑桥英语
lhldsg12 小时前
从零构建智慧场馆解决方案小程序:开发全流程实战
java·前端·小程序
逐流人12 小时前
华为云服务全景指南:从基础架构到运维实践
运维·华为云·云计算·云服务·ecs