Nest.js 文件分片上传:当大文件来敲门,别慌,我们有“分尸”妙计!

场景再现:那个让服务器瑟瑟发抖的下午

记得那天,产品经理笑眯眯地走过来:"咱们加个功能,用户要上传高清企业宣传片,也就10个G左右..."

我当时的表情:(⊙_⊙)

服务器当时的表情:Σ(°△°|||)︴

但作为一名优秀的Nest.js开发者,我露出了神秘的微笑:"没问题,咱们来'分尸'...啊不,分片处理!"

为什么要分片?因为服务器也不是超级计算机啊!

想象一下,你要把一头大象塞进冰箱...等等,这个比喻太老套了。换个说法:你要把《指环王》三部曲加长版一次性通过门缝塞进房间,门会哭的!

同理,大文件直接上传会导致:

  • 服务器内存爆炸(想象一下气球吹到极限)
  • 网络连接像初恋一样脆弱(说断就断)
  • 用户体验堪比看缓冲中的视频------卡到怀疑人生

Nest.js分片上传:像吃披萨一样优雅

第一步:把大文件切成"披萨片"

ts 复制代码
// 当大文件来袭,我们的态度是:切它!
@Post('upload-chunk')
async uploadChunk(@UploadedFile() file, @Body() body) {
  const { chunkNumber, totalChunks, filename } = body;
  
  // 每个chunk就像一块披萨,单独处理不会噎着
  return this.uploadService.saveChunk({
    chunkNumber,
    totalChunks, 
    filename,
    data: file.buffer
  });
}

第二步:给披萨片编号,免得拼回去时变成抽象画

ts 复制代码
// 想象一下拼图时找不到最后一块的绝望...所以我们这样:
async saveChunk(chunkData: ChunkData) {
  // 给每个chunk一个VIP座位号
  const chunkPath = `/tmp/${chunkData.filename}.part${chunkData.chunkNumber}`;
  
  // 万一服务器突然想重启,我们也不怕!
  await this.ensureDirExists(path.dirname(chunkPath));
  
  // 把chunk存起来,像收藏邮票一样认真
  await fs.promises.writeFile(chunkPath, chunkData.data);
}

第三步:当所有披萨片到齐,开始组装!

ts 复制代码
@Post('merge-chunks')
async mergeChunks(@Body() body) {
  const { filename, totalChunks } = body;
  
  // 就像拼乐高,但要小心别踩到碎片
  const mergePath = `/uploads/${filename}`;
  const writeStream = fs.createWriteStream(mergePath);
  
  for (let i = 1; i <= totalChunks; i++) {
    // 按顺序读取每一片,防止视频变成抽象艺术
    const chunkPath = `/tmp/${filename}.part${i}`;
    const chunkBuffer = await fs.promises.readFile(chunkPath);
    writeStream.write(chunkBuffer);
    
    // 清理现场,我们是有洁癖的程序员
    await fs.promises.unlink(chunkPath);
  }
  
  writeStream.end();
  return { message: '文件拼装完成,堪比变形金刚合体!' };
}

分片上传的超级福利

1. 断点续传:网络断了?喝杯咖啡继续!

ts 复制代码
// 检查哪些chunk已经上传了,就像看书夹书签
@Get('upload-progress/:filename')
async getProgress(@Param('filename') filename: string) {
  // 扫描已经到位的chunk
  const uploadedChunks = await this.findExistingChunks(filename);
  return { uploadedChunks, message: '欢迎回来,我们等你很久了!' };
}

2. 并行上传:多条车道就是比单行道快!

ts 复制代码
// 同时上传多个chunk,就像超市开多个收银台
async uploadMultipleChunks(chunks: ChunkData[]) {
  //  Promise.all就是我们的多线程魔法
  return Promise.all(chunks.map(chunk => this.uploadChunk(chunk)));
}

3. 进度条:给用户一种"一切尽在掌握"的错觉

ts 复制代码
// 进度条是程序员给用户的心理按摩
calculateProgress(uploadedChunks: number, totalChunks: number) {
  const progress = (uploadedChunks / totalChunks) * 100;
  return {
    progress: Math.round(progress),
    message: this.generateEncouragingMessage(progress)
  };
}

private generateEncouragingMessage(progress: number) {
  if (progress < 50) return "正在努力搬运数据...";
  if (progress < 80) return "过半了,坚持就是胜利!";
  if (progress < 100) return "快好了,准备放鞭炮!";
  return "上传完成,服务器表示情绪稳定!";
}

真实案例:那个10GB视频的结局

当产品经理再次过来询问进度时,我优雅地展示了分片上传的成果:

  • ✅ 文件被切成1024KB的小chunk
  • ✅ 支持暂停续传(用户可以去上个厕所)
  • ✅ 实时进度条(缓解用户的焦虑)
  • ✅ 服务器内存保持稳定(没有爆炸)

产品经理:"太棒了!那用户说下次想上传20GB的..."

我:"...(╯°□°)╯︵ ┻━┻"

总结:分片上传,真香!

就像不能一口吃掉整个汉堡(虽然有些人尝试过),服务器也不能一次性处理超大文件。分片上传让我们的应用:

  1. ​更健壮​:不怕网络抽风
  2. ​更友好​:给用户进度反馈
  3. ​更高效​:并行处理加速上传

所以下次遇到大文件,别硬刚,学会"分而治之"------这是从秦始皇到Nest.js开发者都懂的智慧!

温馨提示:本文代码经过简化,实际使用记得加错误处理,否则文件上传失败时,错误信息可能会比你的头发还乱!

相关推荐
__zRainy__6 小时前
Node.js Web 框架选型指南:从 Express 到 Hono 的全景对比
前端·node.js·express·koa·nestjs·egg·fastify
__zRainy__11 小时前
Node系列 · Node基础:net 模块
后端·node.js
嘟嘟071719 小时前
从浏览器到 hello world:Docker + nginx 反向代理 + Node 最小服务器一次讲清
docker·容器·node.js
FungLeo19 小时前
Node 后端实战 · 老系统数据迁移怎么不出乱子?V1→V2 重构实战与 3 个生产坑
node.js·serverless·数据库迁移·d1 数据库
今日无bug19 小时前
Tool Use 工具调用 —— 让LLM缸中大脑突破物理限制的3个阶段
node.js·llm·agent
To_OC1 天前
装完 ESLint 它一声不吭?我还以为代码写得多好
后端·node.js·eslint
FungLeo1 天前
Node 后端实战 · 列表查询到底怎么写?一个通用 DSL 封装,过滤分页排序一次搞定
数据库·node.js·serverless·dsl 封装·通用列表查询
_codemonster1 天前
npm run dev 是在开发模式运行,怎么在生产环境运行
前端·npm·node.js
渔夫正在掘金2 天前
Cordis 插件热插拔能力深度解析
后端·node.js
嘟嘟07172 天前
NestJS 入门:从 NestFactory 入口到 Module/Controller/Service 模块化结构一次讲清
typescript·node.js·nestjs