☰
使用 Artillery 进行 HTTP 文件上传压测:基于 formData 与 fromFile 的完整实战指南
2026/9/25 5:38:02 网站建设 项目流程
  • 性能测试
  • 接口测试
  • CLI

【免费下载链接】artillery

The complete load testing platform. Everything you need for production-grade load tests. Serverless & distributed. Load test with Playwright. Load test HTTP APIs, GraphQL, WebSocket, and more. Use any Node.js module.

项目地址:https://gitcode.com/gh_mirrors/ar/artillery
点击查看免费下载

本指南以 Artillery 仓库中的 http-file-uploads 示例 为核心,讲解如何在 Artillery 测试脚本中通过formData与fromFile完成 multipart/form-data 文件上传的压测,包括配套 Express 文件接收服务器的搭建、测试脚本编写、随机文件轮换、上传文件清理以及底层源码实现原理。读完本文,你将能够独立编写并运行一个可复用的 HTTP 文件上传负载测试场景。

示例整体结构

该示例位于仓库的 examples/http-file-uploads 目录,包含四个核心组成部分:

文件作用
app.js基于 Express + Multer 的文件上传接收服务器(监听 3000 端口)
file-uploads.ymlArtillery 测试脚本,演示如何上传文件
files/存放待上传的样例文件(jpg / pdf / png 各一个)
uploads/服务器接收上传后存储文件的目录(运行时生成)
package.json定义服务器启动、测试运行、上传目录清理等 npm 脚本

搭建并启动文件接收服务器

安装依赖

示例自带了 Express 应用(app.js),先安装其依赖:

npm install

安装完成后启动 HTTP 服务器:

npm run app:start

该命令实际执行node app.js(见 package.json 中的scripts定义),启动后服务监听于http://localhost:3000/。

服务器实现解析

接收端服务器代码非常精简,使用 multer 中间件处理 multipart 文件上传:

const express = require('express'); const app = express(); const upload = require('multer')({ dest: 'uploads/', preservePath: true }); const port = 3000; app.post('/upload', upload.single('document'), (req, res) => { const { originalname, mimetype, size } = req.file; res.json({ originalname, mimetype, size }); }); app.listen(port, () => { console.log(`App listening at http://localhost:${port}`); });

关键点:

  • Multer 将上传文件落盘到uploads/目录;
  • 路由/upload接收字段名为document的单个文件(upload.single('document')),与测试脚本中formData.document.fromFile的字段名一一对应;
  • 服务器响应返回originalname、mimetype、size三个 JSON 字段,方便在压测脚本中通过capture断言上传是否成功。

编写文件上传测试脚本

示例测试脚本 file-uploads.yml 是理解 Artillery 文件上传用法的核心:

config: target: "http://localhost:3000" phases: - duration: 10min arrivalRate: 25 # 通过 variables 定义待上传文件的文件名列表, # 这些文件放置在 /files 目录下。 variables: filename: - "artillery-logo.jpg" - "artillery-installation.pdf" - "sre-fundamental-rules.png" scenarios: - flow: # HTTP 服务器有一个 POST /upload 端点,通过 document 字段接收文件 - post: url: "/upload" formData: document: # fromFile 属性指示 Artillery 上传指定文件; # 若文件无法读取,场景会报 ENOENT 错误。 fromFile: "./files/{{ filename }}"

该脚本展示了文件上传压测的三大要素:

1. 负载配置(config.phases)

duration: 10min+arrivalRate: 25表示持续 10 分钟、每秒启动 25 个虚拟用户,模拟每分钟 1500 次上传请求的中等压力。arrivalRate是 Artillery 的固定到达率模型,实际请求量还受每个场景内请求数量与思考时间影响。

2. 随机文件轮换(variables)

variables.filename定义了 3 个文件名,Artillery 在每次场景运行时从列表中随机抽取一个值,与模板语法{{ filename }}结合后拼出完整路径./files/artillery-logo.jpg、./files/artillery-installation.pdf、./files/sre-fundamental-rules.png,从而实现"每次上传随机文件"的效果。相比固定上传同一文件,这种随机化更贴近真实用户的上传行为,也避免服务器缓存造成测试失真。若需要按权重分配文件名,可使用weightedPick类权重机制进一步扩展。

3. multipart 表单字段(formData)

  • document是表单字段名,需与服务器端upload.single('document')严格对应;
  • fromFile指定待上传文件路径。路径是相对于测试脚本所在目录解析的(见下文源码分析),示例中即examples/http-file-uploads/files/;
  • 路径支持模板表达式,因此可配合变量实现动态选择文件。

运行压测

确保服务器已启动后,在示例目录执行:

artillery run file-uploads.yml

运行期间 Artillery 会按phases配置的速率持续向POST /upload发起 multipart 上传请求。仓库集成测试中的 http-file-upload.yml 展示了更完整的用法——同一请求中混合普通文本字段与多个fromFile文件字段:

formData: name: "Artillery" logo: fromFile: "./files/artillery-logo.jpg" guide: fromFile: "./files/{{ filename }}"

即formData中既可以有纯文本字段(name),也可以有多个fromFile文件字段,字段名各不相同即可。集成测试还通过afterResponse钩子对上传响应进行断言校验(见 http-file-upload-processor.js),可用于功能验证类场景。

清理上传目录

测试过程中上传的文件会持续写入服务器的uploads/目录,为方便清理,示例提供了:

npm run uploads:clean

该命令实际执行del-cli 'uploads/*' '!uploads/.keep'(见 package.json),删除目录内所有上传文件但保留.keep占位文件。也可在服务器代码中接入定期清理策略(如按时间轮转目录),避免压测期间磁盘被写满。

底层实现原理:formData 与 fromFile 是如何工作的

从源码层面深入理解这两个关键字,有助于排查路径、字段、Content-Type 等问题。

HTTP 引擎中的 multipart 处理

文件上传由 HTTP 引擎 engine_http.ts 实现。当检测到params.formData时,引擎按以下流程处理:

  1. 创建FormData实例(new FormData())承载 multipart 数据;
  2. 遍历formData的每个字段,先对值执行模板插值template(v, context);
  3. 若字段值是一个普通对象(_.isPlainObject):
    • 存在contentType时,将其作为该字段的 Content-Type 选项(例如 JSON 字段指定application/json);
    • 存在fromFile时,将fromFile路径解析为绝对路径(基于context.vars.$scenarioFile所在目录,即测试脚本所在目录),并用fs.createReadStream(absPath)创建文件读取流作为字段值;
    • 存在value时,直接取该值作为普通文本字段内容;
  4. 调用acc.append(k, V, options)将字段追加到 multipart body,最终作为请求体发送。

相对路径的解析基准

注意fromFile的路径是相对测试脚本文件(.yml)所在目录解析的,而非当前工作目录。示例脚本位于 examples/http-file-uploads/file-uploads.yml,因此./files/{{ filename }}指向examples/http-file-uploads/files/。如果你的测试脚本放在其他目录,务必确认files/的相对位置正确。

Content-Length 与 ENOENT 行为

  • 上传的文件流由 Node 的fs.createReadStream创建,若文件不存在,读取流会触发ENOENT错误——这正是原文档提示"如果文件无法读取,该场景将报告 ENOENT 错误"的原因;
  • 引擎支持通过setContentLengthHeader: true显式设置Content-Length头(见 types.d.ts 与 engine_http.ts),对某些严格要求长度头的服务器很有用;设置失败时引擎仅记录 debug 日志,不会中断测试。

类型与 Schema 定义

在 types.d.ts 中,formData被定义为Record<string, unknown>,注释明确其为 "Multipart form (multipart/form-data),e.g. for file uploads";setContentLengthHeader为可选布尔值。配置 schema 侧(engines/http.js)也预留了formData的校验入口。

单元测试佐证

仓库的 HTTP 引擎单元测试 engine_http.test.js 专门覆盖了formDatamultipart 场景,验证了:

  • 普通文本字段(activity、type、location)以Content-Disposition: form-data发送;
  • 带contentType: 'application/json'的对象字段会附加对应 Content-Type;
  • 字段值支持模板变量({{ activity }}、{{ climate.temperature }}等)。

这从测试层面印证了formData三种字段形态(纯文本、value+contentType、fromFile文件流)的完整语义。

常见问题与排查思路

问题现象可能原因与处理
场景报 ENOENTfromFile路径错误或文件不存在;检查路径相对测试脚本所在目录是否正确
服务器收不到文件formData字段名与服务器(如upload.single('document'))不一致
中文/特殊字符文件名乱码检查服务器端 multipart 解析配置与原文件名编码
上传文件堆积占满磁盘定期执行npm run uploads:clean,或按轮转策略管理uploads/目录
需要为文件字段指定 Content-Type使用contentType属性(源码见 engine_http.ts)

小结

通过 http-file-uploads 示例,可以掌握 Artillery 进行文件上传压测的完整链路:Express + Multer 接收服务器搭建、formData+fromFile脚本编写、variables随机文件轮换、模板路径插值,以及setContentLengthHeader等进阶选项。结合 engine_http.ts 的源码实现与单元测试,即可在实际项目中复现并对抗文件上传接口的性能瓶颈。

  • 性能测试
  • 接口测试
  • CLI

【免费下载链接】artillery

The complete load testing platform. Everything you need for production-grade load tests. Serverless & distributed. Load test with Playwright. Load test HTTP APIs, GraphQL, WebSocket, and more. Use any Node.js module.

项目地址:https://gitcode.com/gh_mirrors/ar/artillery
点击查看免费下载
上一篇:lightweight-charts 价格刻度(Price Scale)完全指南:坐标映射、模式切换与 overlay 刻度管理
下一篇:Cytoscape.js 元素类名闪烁 flashClass 详解:临时高亮与视觉反馈的实现原理与实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询