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开发者都懂的智慧!

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

相关推荐
星空下的曙光16 小时前
Node.js crypto模块所有 API 详解 + 常用 API + 使用场景
算法·node.js·哈希算法
青灬河21 小时前
实现企业级全栈应用服务框架-Elpis(一)
vue.js·node.js
星空下的曙光1 天前
Node.js events模块所有 API 详解 + 常用 API + 使用场景
node.js
无责任此方_修行中1 天前
我的两次 Vibe Coding 经历,一次天堂,一次地狱
后端·node.js·vibecoding
程序铺子1 天前
如何使用 npm 安装 sqlite3 和 canvas 这些包
javascript·npm·node.js
星空下的曙光1 天前
Node.js 事件循环(Event Loop)
node.js
勤奋菲菲2 天前
Egg.js 完全指南:企业级 Node.js 应用框架
开发语言·javascript·node.js
aesthetician2 天前
Node.js 24.10.0: 拥抱现代 JavaScript 与增强性能
开发语言·javascript·node.js
APItesterCris2 天前
Node.js/Python 实战:编写一个淘宝商品数据采集器
大数据·开发语言·数据库·node.js