前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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

# 基于express+muter的文件上传

30 秒速记

  • 标题里的 muter 指的是 multer,Express 官方维护的 multipart/form-data 解析中间件
  • 前端:<form method="post" enctype="multipart/form-data">,或者 FormData + fetch
  • 后端:upload.single('logo') / upload.array('logo', 2) / upload.fields([...]),结果挂在 req.file / req.files
  • 字段名必须前后端一致,不一致直接报 Unexpected field
  • 生产必配:limits 限大小和数量、fileFilter 限类型、服务端生成文件名、错误中间件兜底

Express 自己不解析文件上传,express.json() 只管 JSON,表单传文件用的是 multipart/form-data 编码,要靠 multer 这类中间件把文件拆出来。 用法很简单:创建一个 upload 实例,按场景挂 single、array 或 fields,路由里从 req.file 或 req.files 拿文件信息。容易出问题的地方是字段名对不上、没限文件大小、错误没被接住,所以我会把 limits、fileFilter 和错误中间件当成标配。multer 1.x 有过几个拒绝服务漏洞,新项目直接用 2.x。

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

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

面试官追问

追问 1前端用 JSON 提交,后端 req.body 里却没有文件,为什么?
参考回答

JSON 里放不了文件内容,浏览器传文件要用 multipart/form-data,fetch 的话就是 body: formData,而且别手动设 Content-Type,让浏览器自己带上 boundary。后端没挂 multer 的话,文件一样拿不到。

追问 2上传接口返回 500,错误信息是 Unexpected field,怎么回事?
参考回答

前端文件控件的 name 和 upload.single('xxx') 里的字段名不一样,或者 array 传的文件数超了上限。统一字段名;错误中间件里判断 err instanceof multer.MulterError,转成 400 加可读提示。

追问 3一个 2GB 的文件传上来,服务会怎样?怎么防?
参考回答

默认不限大小,会一直往磁盘写,磁盘和带宽都被吃掉。配 limits: { fileSize: 5 * 1024 * 1024, files: 5 },超了 multer 直接中断并抛 LIMIT_FILE_SIZE;Nginx 前面再加一层 client_max_body_size。

追问 4multer 全局挂在 app.use 上好不好?
参考回答

不好,只挂在需要上传的路由上。全局挂意味着所有接口都会尝试解析 multipart,攻击者可以往任意接口塞文件,临时文件还没人清理。

# 概览

30 秒速记

  • Node.js 上传主流两个选择:multer(Express 中间件)、formidable(不绑框架)
  • 底层都是解析 multipart/form-data 请求体,用流式处理,不会把整个文件塞进内存
  • 用了库只解决了「收到文件」,校验大小、类型、文件名、存储位置还是业务的事
  • Koa 生态常用 @koa/multer 或 koa-body,NestJS 内置封装的就是 multer

Node.js 做上传不用自己解析请求体,社区里 multer 和 formidable 都很成熟,Express 项目一般直接用 multer。 它们干的是同一件事:把 multipart/form-data 这种用分隔符拼起来的请求体拆开,文件流式写到磁盘或内存,普通字段放进 req.body。区别主要在接入方式,multer 是标准的 Express 中间件,formidable 是独立的解析器,哪个框架都能用。选完库只是开始,限制大小、过滤类型、生成安全的文件名这些都得自己配。

图片上传是web开发中经常用到的功能,node社区在这方面也有了相对完善的支持。

常用的开源组件有multer、formidable等,借助这两个开源组件,可以轻松搞定图片上传。

面试官追问

追问 1商品图片上传用了 multer,是不是就安全了?
参考回答

不是,multer 默认什么都收。要自己配 fileFilter 只放图片类型,配 limits 限大小,敏感场景再读文件头的魔数确认真的是图片,mimetype 是客户端说了算的。

追问 2multer 和 formidable 怎么选?
参考回答

Express 或 NestJS 项目用 multer,挂路由上就行;不用 Express、或者想自己控制解析流程(比如边收边传到对象存储),formidable 更灵活。两者性能差别不是选型重点。

追问 3需求从单张头像改成最多 9 张图,只把 single 改成 array 就够了吗?
参考回答

还要定:单张和总大小上限、返回结构从对象变数组、第 5 张失败了前 4 张算不算。前端也得改成同一个字段名追加多个文件,后端读 req.files 而不是 req.file。

追问 4大文件上传 multer 扛得住吗?
参考回答

几十兆以内没问题,它是流式写盘的。再大就该做分片上传和断点续传,或者让前端拿签名地址直传对象存储,别让业务服务器扛流量。

# 环境初始化

30 秒速记

  • 依赖就两个:npm install express multer(--save 在 npm 5 之后已经是默认行为)
  • 示例结构:app.js 处理上传,form.html 提供上传表单
  • 读文件用 path.join(__dirname, 'form.html'),别依赖进程启动目录
  • 把启动命令写进 package.json 的 scripts,本地和 CI 跑同一条

环境准备很简单,装 express 和 multer 两个包,然后一个 app.js 写服务端、一个 form.html 写上传表单。 命令里同一个包写两遍不会装两次,npm 会自动去重,package.json 里也只有一条。--save 从 npm 5 开始就是默认的,现在可以不写。有个小坑值得提前避开:读 form.html 用的是相对路径 ./form.html,它相对的是你在哪个目录执行 node,换个目录启动就找不到文件,用 path.join(__dirname, 'form.html') 最稳。

非常简单,一行命令。

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

每个示例下面,都有下面两个文件

➜  upload-custom-filename git:(master) ✗ tree -L 1
.
├── app.js # 服务端代码,用来处理文件上传请求
├── form.html # 前端页面,用来上传文件
@前端进阶之旅: 代码已经复制到剪贴板

面试官追问

追问 1npm install express multer multer 装了两份 multer 吗?
参考回答

没有,重复的包名会被去重,node_modules 里只有一份,package.json 里也只记一条。写重复了不报错而已。

追问 2在项目目录跑 node app.js 正常,在上一级跑 node demo/app.js 就报找不到 form.html,为什么?
参考回答

fs.readFileSync('./form.html') 的相对路径是相对 process.cwd(),不是相对 app.js 所在目录。改成 path.join(__dirname, 'form.html') 就和启动位置无关了;multer 的 dest: 'upload/' 也是同一个问题。

追问 3要让同事一键复现这个上传示例,还缺什么?
参考回答

缺依赖锁文件和启动脚本。把 "start": "node app.js" 写进 scripts,锁文件一起提交,端口写进 README 或者读环境变量,别人 npm ci && npm start 就能跑。

追问 4form.html 直接用 express.static 托管行不行?
参考回答

完全可以,而且比每次请求 readFileSync 更好:app.use(express.static(path.join(__dirname, 'public'))),还自带缓存头。示例那样写只是为了少讲一个概念。

# 基础例子:单图上传

30 秒速记

  • 后端:multer({ dest: 'upload/' }) + upload.single('logo')
  • 前端:method="post" + enctype="multipart/form-data" + <input type="file" name="logo">,三个缺一不可
  • 文件信息在 req.file,普通文本字段在 req.body
  • dest 模式下文件名是随机 32 位十六进制,不带扩展名;目录不存在 multer 会自己建
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

基于express+muter的文件上传|Nodejs专题系列

# 基于express+muter的文件上传

30 秒速记

  • 标题里的 muter 指的是 multer,Express 官方维护的 multipart/form-data 解析中间件
  • 前端:<form method="post" enctype="multipart/form-data">,或者 FormData + fetch
  • 后端:upload.single('logo') / upload.array('logo', 2) / upload.fields([...]),结果挂在 req.file / req.files
  • 字段名必须前后端一致,不一致直接报 Unexpected field
  • 生产必配:limits 限大小和数量、fileFilter 限类型、服务端生成文件名、错误中间件兜底

Express 自己不解析文件上传,express.json() 只管 JSON,表单传文件用的是 multipart/form-data 编码,要靠 multer 这类中间件把文件拆出来。 用法很简单:创建一个 upload 实例,按场景挂 single、array 或 fields,路由里从 req.file 或 req.files 拿文件信息。容易出问题的地方是字段名对不上、没限文件大小、错误没被接住,所以我会把 limits、fileFilter 和错误中间件当成标配。multer 1.x 有过几个拒绝服务漏洞,新项目直接用 2.x。

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

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

面试官追问

追问 1前端用 JSON 提交,后端 req.body 里却没有文件,为什么?
参考回答

JSON 里放不了文件内容,浏览器传文件要用 multipart/form-data,fetch 的话就是 body: formData,而且别手动设 Content-Type,让浏览器自己带上 boundary。后端没挂 multer 的话,文件一样拿不到。

追问 2上传接口返回 500,错误信息是 Unexpected field,怎么回事?
参考回答

前端文件控件的 name 和 upload.single('xxx') 里的字段名不一样,或者 array 传的文件数超了上限。统一字段名;错误中间件里判断 err instanceof multer.MulterError,转成 400 加可读提示。

追问 3一个 2GB 的文件传上来,服务会怎样?怎么防?
参考回答

默认不限大小,会一直往磁盘写,磁盘和带宽都被吃掉。配 limits: { fileSize: 5 * 1024 * 1024, files: 5 },超了 multer 直接中断并抛 LIMIT_FILE_SIZE;Nginx 前面再加一层 client_max_body_size。

追问 4multer 全局挂在 app.use 上好不好?
参考回答

不好,只挂在需要上传的路由上。全局挂意味着所有接口都会尝试解析 multipart,攻击者可以往任意接口塞文件,临时文件还没人清理。

# 概览

30 秒速记

  • Node.js 上传主流两个选择:multer(Express 中间件)、formidable(不绑框架)
  • 底层都是解析 multipart/form-data 请求体,用流式处理,不会把整个文件塞进内存
  • 用了库只解决了「收到文件」,校验大小、类型、文件名、存储位置还是业务的事
  • Koa 生态常用 @koa/multer 或 koa-body,NestJS 内置封装的就是 multer

Node.js 做上传不用自己解析请求体,社区里 multer 和 formidable 都很成熟,Express 项目一般直接用 multer。 它们干的是同一件事:把 multipart/form-data 这种用分隔符拼起来的请求体拆开,文件流式写到磁盘或内存,普通字段放进 req.body。区别主要在接入方式,multer 是标准的 Express 中间件,formidable 是独立的解析器,哪个框架都能用。选完库只是开始,限制大小、过滤类型、生成安全的文件名这些都得自己配。

图片上传是web开发中经常用到的功能,node社区在这方面也有了相对完善的支持。

常用的开源组件有multer、formidable等,借助这两个开源组件,可以轻松搞定图片上传。

面试官追问

追问 1商品图片上传用了 multer,是不是就安全了?
参考回答

不是,multer 默认什么都收。要自己配 fileFilter 只放图片类型,配 limits 限大小,敏感场景再读文件头的魔数确认真的是图片,mimetype 是客户端说了算的。

追问 2multer 和 formidable 怎么选?
参考回答

Express 或 NestJS 项目用 multer,挂路由上就行;不用 Express、或者想自己控制解析流程(比如边收边传到对象存储),formidable 更灵活。两者性能差别不是选型重点。

追问 3需求从单张头像改成最多 9 张图,只把 single 改成 array 就够了吗?
参考回答

还要定:单张和总大小上限、返回结构从对象变数组、第 5 张失败了前 4 张算不算。前端也得改成同一个字段名追加多个文件,后端读 req.files 而不是 req.file。

追问 4大文件上传 multer 扛得住吗?
参考回答

几十兆以内没问题,它是流式写盘的。再大就该做分片上传和断点续传,或者让前端拿签名地址直传对象存储,别让业务服务器扛流量。

# 环境初始化

30 秒速记

  • 依赖就两个:npm install express multer(--save 在 npm 5 之后已经是默认行为)
  • 示例结构:app.js 处理上传,form.html 提供上传表单
  • 读文件用 path.join(__dirname, 'form.html'),别依赖进程启动目录
  • 把启动命令写进 package.json 的 scripts,本地和 CI 跑同一条

环境准备很简单,装 express 和 multer 两个包,然后一个 app.js 写服务端、一个 form.html 写上传表单。 命令里同一个包写两遍不会装两次,npm 会自动去重,package.json 里也只有一条。--save 从 npm 5 开始就是默认的,现在可以不写。有个小坑值得提前避开:读 form.html 用的是相对路径 ./form.html,它相对的是你在哪个目录执行 node,换个目录启动就找不到文件,用 path.join(__dirname, 'form.html') 最稳。

非常简单,一行命令。

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

每个示例下面,都有下面两个文件

➜  upload-custom-filename git:(master) ✗ tree -L 1
.
├── app.js # 服务端代码,用来处理文件上传请求
├── form.html # 前端页面,用来上传文件
@前端进阶之旅: 代码已经复制到剪贴板

面试官追问

追问 1npm install express multer multer 装了两份 multer 吗?
参考回答

没有,重复的包名会被去重,node_modules 里只有一份,package.json 里也只记一条。写重复了不报错而已。

追问 2在项目目录跑 node app.js 正常,在上一级跑 node demo/app.js 就报找不到 form.html,为什么?
参考回答

fs.readFileSync('./form.html') 的相对路径是相对 process.cwd(),不是相对 app.js 所在目录。改成 path.join(__dirname, 'form.html') 就和启动位置无关了;multer 的 dest: 'upload/' 也是同一个问题。

追问 3要让同事一键复现这个上传示例,还缺什么?
参考回答

缺依赖锁文件和启动脚本。把 "start": "node app.js" 写进 scripts,锁文件一起提交,端口写进 README 或者读环境变量,别人 npm ci && npm start 就能跑。

追问 4form.html 直接用 express.static 托管行不行?
参考回答

完全可以,而且比每次请求 readFileSync 更好:app.use(express.static(path.join(__dirname, 'public'))),还自带缓存头。示例那样写只是为了少讲一个概念。

# 基础例子:单图上传

30 秒速记

  • 后端:multer({ dest: 'upload/' }) + upload.single('logo')
  • 前端:method="post" + enctype="multipart/form-data" + <input type="file" name="logo">,三个缺一不可
  • 文件信息在 req.file,普通文本字段在 req.body
  • dest 模式下文件名是随机 32 位十六进制,不带扩展名;目录不存在 multer 会自己建
  • 示例无条件返回成功,实际要先判断 req.file 是否存在

单图上传就是在路由前面挂一个 upload.single('logo'),它会去请求里找名叫 logo 的文件字段,存到 dest 目录,再把文件信息放到 req.file。 前端表单那边三样东西必须齐:post 方法、multipart/form-data 编码、文件控件的 name 叫 logo,少了 enctype 浏览器只会把文件名当字符串发过去。存下来的文件名是一串随机字符、没有后缀,这是 multer 故意的,避免用户上传的名字搞出路径问题。另外示例路由不管有没有收到文件都回成功,真实接口我会先判断 req.file。

← 使用 superagent 与 cheerio 完成简单爬虫将图片转成datauri嵌入到html →

fe