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

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

相关推荐
半桶水专家8 小时前
npm run 的工作原理和工作流程
前端·npm·node.js
Q_Q19632884759 小时前
python+django/flask基于深度学习的个性化携程美食数据推荐系统
spring boot·python·深度学习·django·flask·node.js·php
Live&&learn9 小时前
nvm切换node版本时,npm不跟着切换解决
前端·npm·node.js
Q_Q51100828510 小时前
python+django/flask的篮球馆/足球场地/运动场地预约系统
spring boot·python·django·flask·node.js·php
Q_Q51100828511 小时前
python+django/flask的城市供水管网爆管预警系统-数据可视化
spring boot·python·django·flask·node.js·php
老前端的功夫18 小时前
Web应用的永生之术:PWA落地与实践深度指南
java·开发语言·前端·javascript·css·node.js
浪裡遊1 天前
Next.js路由系统
开发语言·前端·javascript·react.js·node.js·js
gihigo19981 天前
使用JavaScript和Node.js构建简单的RESTful API
javascript·node.js·restful
labview_自动化1 天前
Node.js
node.js
liangshanbo12151 天前
使用 nvm 安装 Node.js
node.js