前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# busboy模块

30 秒速记

  • busboy 是专门解析 multipart/form-data 的流式解析器,边收边吐,文件不会整块进内存
  • 它只管「拆」:文本字段走 field 事件,文件走 file 事件并交给你一个可读流,存哪由你决定
  • 每个文件流必须被消费(pipe 出去或 resume()),否则整个解析卡住,结束事件永远不来
  • 默认没有大小和数量上限,limits 里的 fileSize、files、fields 要自己配
  • 版本差异:0.x 是 new Busboy() + finish;1.x 改成 busboy() 函数 + close,file 回调参数也变成 (name, stream, info)

busboy 是一个流式的 multipart 解析器,它把上传请求拆成「字段」和「文件流」两类事件交给你处理。 浏览器用表单传文件时,请求体是一段用 boundary 分隔的二进制数据,busboy 一边读请求流一边切,所以传一个 1GB 的视频,进程内存也只占缓冲区那点。它的定位很底层,不帮你落盘、不帮你限流、不帮你改名,multer、koa-body 这类上层中间件内部用的就是它或者同类库。用它最容易踩的坑是不想要某个文件就直接 return,那个流没人读,解析会一直挂着。

理解「busboy模块」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

busboy 在 2022 年发布 1.0 时改了 API,网上大量教程还是 0.x 写法,两者放一起对照最清楚。

// busboy 0.x
const Busboy = require('busboy')
const bb = new Busboy({ headers: req.headers })
bb.on('file', (fieldname, file, filename, encoding, mimetype) => {
  file.pipe(fs.createWriteStream(`/tmp/${Date.now()}`))
})
bb.on('field', (fieldname, val, fieldnameTruncated, valTruncated) => {})
bb.on('finish', () => res.end('ok'))
req.pipe(bb)
@前端进阶之旅: 代码已经复制到剪贴板
// busboy 1.x
const busboy = require('busboy')
const { randomUUID } = require('crypto')

const bb = busboy({
  headers: req.headers,
  limits: { fileSize: 5 * 1024 * 1024, files: 1, fields: 10 },
})
bb.on('file', (name, file, info) => {
  const { filename, mimeType } = info          // 第三个参数是对象
  if (!mimeType.startsWith('image/')) return file.resume() // 不要的也得读掉
  file.on('limit', () => console.log('超过 5MB,被截断'))
  file.pipe(fs.createWriteStream(`/tmp/${randomUUID()}`))
})
bb.on('field', (name, val, info) => {
  // info.nameTruncated / info.valueTruncated 表示是否被 limits 截断
})
bb.on('close', () => res.end('ok'))           // 结束事件叫 close
req.pipe(bb)
@前端进阶之旅: 代码已经复制到剪贴板

记住三处变化就够了:构造方式从 new Busboy() 变成调用 busboy();file / field 回调把元信息收进 info 对象;结束事件从 finish 换成 close。另外 1.x 遇到缺失或不支持的 Content-Type 会在构造时直接抛错,外面要包 try/catch 返回 415。

面试官追问

追问 1Koa 里已经用了 koa-bodyparser,为什么 ctx.request.body 拿不到上传的文件?
参考回答

koa-bodyparser 只处理 json、form、text 三种类型,multipart/form-data 它直接跳过。文件上传得换 busboy、koa-body(开 multipart: true)或者 @koa/multer 这类专门的解析器。

追问 2用户传一个 2GB 的文件,busboy 会不会把内存撑爆?
参考回答

正常不会,它是流式的,file.pipe(fs.createWriteStream(path)) 自带背压,磁盘写慢了就暂停读。会出事的是你在 data 事件里把 chunk 全 push 进数组再 Buffer.concat,那就又退回整块进内存了。

追问 3表单里有三个文件,业务只要第一个,后面两个不处理会怎样?
参考回答

请求会卡住。busboy 要把每个文件流读完才能继续往后解析,没人读的流把整个管道堵死,结束事件不触发,接口一直 pending。不要的文件调一句 file.resume() 把数据丢掉就行。

追问 4直接用 busboy 和用 multer / koa-body,怎么选?
参考回答

一般业务我直接用上层中间件,限制、临时目录、错误处理都包好了。只有要把文件流直接转发到对象存储、边传边算哈希这类需要精确控制流的场景,才值得自己写 busboy。

追问 5照着老教程写的 new Busboy({ headers }),升级到 1.x 后报 filename.split is not a function,为什么?
参考回答

1.x 的 file 事件签名变成了 (name, stream, info),第三个参数是 { filename, encoding, mimeType } 对象,不再是字符串。另外结束事件改用 close。要么锁回 0.3.1,要么按新签名改代码。

# busboy模块

# 快速开始

30 秒速记

  • 最小链路三步:用请求头建解析器 → 挂 file / field / 结束事件 → req.pipe(bb)
  • 在 Koa 里喂的是原生 ctx.req,不是 ctx.request
  • 结束事件触发后再响应,响应前别忘了文件写流也要写完
  • 本地测试不用写页面:curl -F "avatar=@a.png" -F "name=tom" localhost:3000/upload
  • Content-Type 不是 multipart 时 1.x 会在构造时抛错,要捕获后回 415

快速跑通 busboy 只需要三步:拿请求头创建解析器、监听事件、把请求流 pipe 进去。 它本身就是一个可写流,所以 req.pipe(bb) 之后数据才开始流动,事件才会陆续触发。在 Koa 里要注意拿的是 ctx.req 这个 Node 原生请求对象。调试阶段我一般不写前端页面,直接用 curl -F 发 multipart 请求,字段和文件都能带,比点页面快得多。跑通之后第一件事是补 limits,默认是不限大小的。

理解「快速开始」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

面试官追问

追问 1用 curl -d 'a=1' 测这个接口,服务直接崩了,为什么?
参考回答

-d 发的是 application/x-www-form-urlencoded,这个 busboy 其实也能解析;真正会崩的是不带 Content-Type 或者发 application/json,1.x 构造时直接抛 Unsupported content type。把 busboy({ headers }) 包进 try/catch,抛错就回 415。

追问 2前端表单里文本字段放在文件后面,后端在 file 事件里读不到 userId,正常吗?
参考回答

正常。multipart 是按表单顺序一段段流过来的,解析到文件时后面的字段还没到。要么前端把 userId 这类字段放在文件前面,要么等结束事件再统一用字段做业务处理。

追问 3Koa 里写成 ctx.request.pipe(bb) 为什么报错?
参考回答

ctx.request 是 Koa 包装出来的对象,不是流,没有 pipe 方法。原生的 IncomingMessage 在 ctx.req 上,写 ctx.req.pipe(bb)。

追问 4在 Koa 的 async 中间件里用 busboy,为什么要包一个 Promise?
参考回答

busboy

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

busboy模块|Nodejs专题系列

# busboy模块

30 秒速记

  • busboy 是专门解析 multipart/form-data 的流式解析器,边收边吐,文件不会整块进内存
  • 它只管「拆」:文本字段走 field 事件,文件走 file 事件并交给你一个可读流,存哪由你决定
  • 每个文件流必须被消费(pipe 出去或 resume()),否则整个解析卡住,结束事件永远不来
  • 默认没有大小和数量上限,limits 里的 fileSize、files、fields 要自己配
  • 版本差异:0.x 是 new Busboy() + finish;1.x 改成 busboy() 函数 + close,file 回调参数也变成 (name, stream, info)

busboy 是一个流式的 multipart 解析器,它把上传请求拆成「字段」和「文件流」两类事件交给你处理。 浏览器用表单传文件时,请求体是一段用 boundary 分隔的二进制数据,busboy 一边读请求流一边切,所以传一个 1GB 的视频,进程内存也只占缓冲区那点。它的定位很底层,不帮你落盘、不帮你限流、不帮你改名,multer、koa-body 这类上层中间件内部用的就是它或者同类库。用它最容易踩的坑是不想要某个文件就直接 return,那个流没人读,解析会一直挂着。

理解「busboy模块」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

busboy 在 2022 年发布 1.0 时改了 API,网上大量教程还是 0.x 写法,两者放一起对照最清楚。

// busboy 0.x
const Busboy = require('busboy')
const bb = new Busboy({ headers: req.headers })
bb.on('file', (fieldname, file, filename, encoding, mimetype) => {
  file.pipe(fs.createWriteStream(`/tmp/${Date.now()}`))
})
bb.on('field', (fieldname, val, fieldnameTruncated, valTruncated) => {})
bb.on('finish', () => res.end('ok'))
req.pipe(bb)
@前端进阶之旅: 代码已经复制到剪贴板
// busboy 1.x
const busboy = require('busboy')
const { randomUUID } = require('crypto')

const bb = busboy({
  headers: req.headers,
  limits: { fileSize: 5 * 1024 * 1024, files: 1, fields: 10 },
})
bb.on('file', (name, file, info) => {
  const { filename, mimeType } = info          // 第三个参数是对象
  if (!mimeType.startsWith('image/')) return file.resume() // 不要的也得读掉
  file.on('limit', () => console.log('超过 5MB,被截断'))
  file.pipe(fs.createWriteStream(`/tmp/${randomUUID()}`))
})
bb.on('field', (name, val, info) => {
  // info.nameTruncated / info.valueTruncated 表示是否被 limits 截断
})
bb.on('close', () => res.end('ok'))           // 结束事件叫 close
req.pipe(bb)
@前端进阶之旅: 代码已经复制到剪贴板

记住三处变化就够了:构造方式从 new Busboy() 变成调用 busboy();file / field 回调把元信息收进 info 对象;结束事件从 finish 换成 close。另外 1.x 遇到缺失或不支持的 Content-Type 会在构造时直接抛错,外面要包 try/catch 返回 415。

面试官追问

追问 1Koa 里已经用了 koa-bodyparser,为什么 ctx.request.body 拿不到上传的文件?
参考回答

koa-bodyparser 只处理 json、form、text 三种类型,multipart/form-data 它直接跳过。文件上传得换 busboy、koa-body(开 multipart: true)或者 @koa/multer 这类专门的解析器。

追问 2用户传一个 2GB 的文件,busboy 会不会把内存撑爆?
参考回答

正常不会,它是流式的,file.pipe(fs.createWriteStream(path)) 自带背压,磁盘写慢了就暂停读。会出事的是你在 data 事件里把 chunk 全 push 进数组再 Buffer.concat,那就又退回整块进内存了。

追问 3表单里有三个文件,业务只要第一个,后面两个不处理会怎样?
参考回答

请求会卡住。busboy 要把每个文件流读完才能继续往后解析,没人读的流把整个管道堵死,结束事件不触发,接口一直 pending。不要的文件调一句 file.resume() 把数据丢掉就行。

追问 4直接用 busboy 和用 multer / koa-body,怎么选?
参考回答

一般业务我直接用上层中间件,限制、临时目录、错误处理都包好了。只有要把文件流直接转发到对象存储、边传边算哈希这类需要精确控制流的场景,才值得自己写 busboy。

追问 5照着老教程写的 new Busboy({ headers }),升级到 1.x 后报 filename.split is not a function,为什么?
参考回答

1.x 的 file 事件签名变成了 (name, stream, info),第三个参数是 { filename, encoding, mimeType } 对象,不再是字符串。另外结束事件改用 close。要么锁回 0.3.1,要么按新签名改代码。

# busboy模块

# 快速开始

30 秒速记

  • 最小链路三步:用请求头建解析器 → 挂 file / field / 结束事件 → req.pipe(bb)
  • 在 Koa 里喂的是原生 ctx.req,不是 ctx.request
  • 结束事件触发后再响应,响应前别忘了文件写流也要写完
  • 本地测试不用写页面:curl -F "avatar=@a.png" -F "name=tom" localhost:3000/upload
  • Content-Type 不是 multipart 时 1.x 会在构造时抛错,要捕获后回 415

快速跑通 busboy 只需要三步:拿请求头创建解析器、监听事件、把请求流 pipe 进去。 它本身就是一个可写流,所以 req.pipe(bb) 之后数据才开始流动,事件才会陆续触发。在 Koa 里要注意拿的是 ctx.req 这个 Node 原生请求对象。调试阶段我一般不写前端页面,直接用 curl -F 发 multipart 请求,字段和文件都能带,比点页面快得多。跑通之后第一件事是补 limits,默认是不限大小的。

理解「快速开始」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

面试官追问

追问 1用 curl -d 'a=1' 测这个接口,服务直接崩了,为什么?
参考回答

-d 发的是 application/x-www-form-urlencoded,这个 busboy 其实也能解析;真正会崩的是不带 Content-Type 或者发 application/json,1.x 构造时直接抛 Unsupported content type。把 busboy({ headers }) 包进 try/catch,抛错就回 415。

追问 2前端表单里文本字段放在文件后面,后端在 file 事件里读不到 userId,正常吗?
参考回答

正常。multipart 是按表单顺序一段段流过来的,解析到文件时后面的字段还没到。要么前端把 userId 这类字段放在文件前面,要么等结束事件再统一用字段做业务处理。

追问 3Koa 里写成 ctx.request.pipe(bb) 为什么报错?
参考回答

ctx.request 是 Koa 包装出来的对象,不是流,没有 pipe 方法。原生的 IncomingMessage 在 ctx.req 上,写 ctx.req.pipe(bb)。

追问 4在 Koa 的 async 中间件里用 busboy,为什么要包一个 Promise?
参考回答

busboy 是事件驱动的,中间件函数不等它就返回了,Koa 会把空 body 当 404 发出去。包成 Promise,在结束事件里 resolve、出错时 reject,外面 await 住再设置 ctx.body。

# 安装

30 秒速记

  • npm install busboy,npm 5 之后 --save 是默认行为,写不写都一样
  • 它在请求时运行,必须进 dependencies,放 devDependencies 的话生产 npm ci --omit=dev 后就没了
  • 当前主版本是 1.x,老教程是 0.x 写法,要么锁 busboy@0.3.1,要么按新 API 写
  • 提交 lockfile,否则下一台机器可能装到另一个主版本
  • TS 项目补 @types/busboy

安装就一句 npm install busboy,真正要留心的是版本和依赖归属。 busboy 在 1.0 改了 API,构造方式、事件参数、结束事件名都变了,跟着老教程写代码又装了新版,就会出现各种 undefined。所以我会看一眼 package.json 里装的是哪个主版本,再决定参考哪份文档。它是运行时依赖,放 dependencies;lockfile 一定要提交,CI 和生产用 npm ci 按锁定版本装。

npm install --save busboy
@前端进阶之旅: 代码已经复制到剪贴板

理解「安装」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

← 6.2 ejs模板引擎7.2 上传文件简单实现 →

fe