然后,使用 mkcert 在本地给这几个域名一键签发受信任的 SSL 证书,留给后端服务使用。
2. 极简的 Node.js 后端
后端不需要复杂的业务逻辑,起个 Express 服务,挂载刚刚签发的证书,写个接口专门接收切片即可。这里唯一要注意的是 cors 跨域配置,因为前端接下来会跨好几个子域名来发请求。
const https = require('https');
const express = require('express');
const cors = require('cors');
const app = express();
// 允许携带 credentials 以及来自不同子域名的跨域请求
app.use(cors({
origin: (origin, callback) => callback(null, true),
credentials: true
}));
app.post('/upload', (req, res) => {
// 省略切片落盘逻辑...
});
// 使用 mkcert 生成的证书启动 HTTPS 服务
https.createServer(sslOptions, app).listen(443);
3. 前端的动态网关调度
平时我们写上传,目标 URL 通常写死为一个。现在我们需要维护一个“域名池”。在遍历分片数组时,通过简单的取模算法,把不同的分片请求均匀地分配给这些不同的子域名。
// 我们预设的上传域名池
const SHARDING_DOMAINS =[
'https://u1.local.com',
'https://u2.local.com',
'https://u6.local.com'
];
async function uploadChunks(chunks) {
const uploadTasks = chunks.map((chunk, index) => {
// 轮询分配域名
const targetDomain = SHARDING_DOMAINS[index % SHARDING_DOMAINS.length];
const url = `${targetDomain}/upload`;
const formData = new FormData();
formData.append('chunk', chunk.file);
return fetch(url, { method: 'POST', body: formData });
});
// 使用并发控制函数(这里省略 p-limit 的实现),最大并发保持 6
await asyncPool(6, uploadTasks);
}
耗时缩短到了 2.44s,吞吐量达到了 616.82 MB/s,速度提升了将近 46% 。
再看底下的请求列表,发往 u1.local.com、u2.local.com 和 u6.local.com 的请求,分别拿到了 三个独立的 TCP 连接 ID(2081087、2082684、2081775)。
事实证明,通过我们在前端引入多域名策略,成功越过了浏览器针对 HTTP/2 的单连接复用机制,在物理层面上拓宽了上传的整体带宽,实现了真正的物理并行传输。
以上实验的完整前后端代码已经提交到了 GitHub,代码比较精简,主要为了提供一个验证思路。欢迎大家在本地跑跑看,或者交流不同的见解。
🔗 前端 Demo 源码: large-file-upload-demo-frontend
🔗 后端 Demo 源码: large-file-upload-demo-backend
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。