你构建了一个接收文件的接口。用户上传头像到 POST /avatars,或者你的应用将已签名的 PDF 推送到 POST /documents。路由逻辑在你的脑海中已经是通的。现在你需要证明它在 HTTP 上也能正常工作:选择一个真实的文件,将其附加到表单字段,发送请求,并检查响应。
这正是许多 API 工具变得繁琐的地方。文件上传使用的是 multipart/form-data(而不是 JSON),因此你不能直接粘贴 body 然后点击发送。你需要一个能够识别文件字段的请求构建器,以及一个在后续运行测试时能够找到该文件的测试 runner。Apifox 两者兼顾。本指南将带你走完整个流程:发送单个文件上传、同时发送文件与 JSON、断言响应,以及没有人警告过你的真实情况——当同样的上传步骤在 Runner 或 CLI 中无头(headless)运行时找不到文件该怎么办。如果你想先了解该格式本身的背景,API 文件上传入门指南中介绍了 multipart 请求的结构构成。对于浏览器端,FormData 的 MDN 参考文档是一个很好的配套读物。
什么是 multipart/form-data 以及为什么上传需要它
API 请求的 body 可以有多种形式。在 Apifox 的请求 Body 区域中,你可以选择 form-data、x-www-form-urlencoded、JSON、XML、raw 或 binary。大多数情况下你会选择 JSON,而文件上传则是例外。
form-data 类型的 body 映射到 Content-Type: multipart/form-data header。它是专为在上传文件的同时传输其他数据而设计的格式。body 不是一整个二进制大对象(blob),而是被分割成多个部分(parts),每个部分都有自己的名称和内容。其中一部分可以是普通的字符串(例如标题),另一部分可以是图片的原始字节。这就是为什么照片上传及其元数据可以在同一个请求中传输的原因。
与其非常相似的是 x-www-form-urlencoded。它在编辑器中看起来很相似,都是在 body 中发送键值对,但它是为不含文件的简单表单设计的。如果你的接口接收文件,你应该选择 form-data。只有在每个字段都是简短的标量值且不涉及字节流时,才使用 x-www-form-urlencoded。
在 form-data 中,Apifox 将每个 parameter 显示为键值对,并且每个 parameter 都带有一个类型:string、integer、file 等。这个针对每个 parameter 的类型设置就是关键所在。将字段设置为 file,Apifox 就会将其值视为要附加的文件,而不是要发送的文本。
发送单个文件上传并断言响应
假设你正在测试 POST /avatars。它接收一个包含图片的字段 avatar,并返回包含存储 URL 的 JSON。以下是操作步骤。
1. 打开 body 区域并选择 form-data。 在你的接口或新建请求中,将方法设置为 POST,并将 URL 设置为你的头像路由。打开 body 标签页并选择 form-data body 类型。Apifox 会为你自动设置 Content-Type: multipart/form-data。
2. 添加文件 parameter 并将其类型设置为 file。 添加一个 key 为 avatar 的 parameter。在 key 旁边,使用类型选择器将其类型从 string 更改为 file。值单元格将变成文件选择器,而不是文本框。
3. 点击 Upload 并选择一个本地文件。 在 avatar 行点击 Upload 并从你的电脑中选择一张图片,例如 jane-profile.png。Apifox 会记录该文件的路径。
4. 发送请求。 点击 Send。Apifox 会从存储的本地路径读取文件,构建 multipart body 并发送它。需要提前了解的是:Apifox 会在请求中发送文件,但不会将文件存储在云端。它只保存本地路径,而不保存字节数据。这个细节在后面很重要,请牢记。
成功调用后会返回如下内容:
{
"id": "usr_8842",
"avatarUrl": "https://cdn.example.com/avatars/usr_8842.png",
"sizeBytes": 48210,
"contentType": "image/png"
}
5. 断言响应。 仅返回 200 的发送本身并不算通过测试。添加断言以进行真正的检查。在 Apifox 中,你可以将这些添加为接口或场景步骤的后置操作断言。简单来说,你想要确认状态码以及 body 中包含一个可用的 URL:
status code == 200
$.avatarUrl exists
$.contentType == "image/png"
这些可以直接映射到 Apifox 的断言 UI:一个针对状态码的断言,一个针对 JSONPath $.avatarUrl 是否存在的断言,以及一个针对 $.contentType 的断言。如果你还不熟悉断言,API 断言指南展示了完整的运算符集以及 JSONPath 如何定位字段。
为了在工具之外进行快速验证,相同的上传在 curl 中如下所示:
curl -X POST https://api.example.com/avatars \
-F "avatar=@jane-profile.png"
-F 标志是 curl 构建 multipart 部分的方式,而 @ 告诉它读取文件内容。Apifox 的 form-data 文件 parameter 使用选择器代替了标志,实现了相同的效果。
同时发送文件和 JSON
实际的接口很少只接收一个纯文件。POST /documents 可能需要文件加上元数据:标题、类别,可能还有一个标签数组。你有两种干净的方法可以在一个 multipart 请求中实现这一点。
简单变体是标量字段。在你的文件字段旁添加更多 form-data parameters,并保持它们的类型为 string 或 integer。一个 title string、一个 category string,以及一个类型设置为 file 的 file。这三个都会在同一个请求中传输。
当元数据是结构化的(如嵌套的 object 或数组)时,你可以将其作为 JSON 在 string 部分中发送。添加一个名为 metadata 的 form-data parameter,保持其类型为 string,然后直接将 JSON 粘贴到值中:
{ "title": "Q3 Invoice", "category": "billing", "tags": ["invoice", "2026", "paid"] }因此该请求包含两个部分:携带 q3-invoice.pdf 的 file(类型为 file),以及携带该 JSON 的 metadata(类型为 string)。服务端会从其中一部分读取文件,并从另一部分解析 JSON。许多公开 API 都采用这种方式进行上传;Stripe 文件上传文档 就是一个真实的 multipart 接口的很好示例,它将文件部分与普通字段配对。这种模式非常常见,以至于 Postman 用户也会遇到;如果您正在进行迁移,关于在 Postman 中上传文件和 JSON 数据的教程可以完美映射到 Apifox 的 form-data 字段中。
需要附加多个文件?只需添加另一个类型为 file 的 parameter。一个接收主文件和缩略图的 POST /documents 接口会有两个文件行:file 和 thumbnail,每一行都有自己的“上传”按钮。这里没有特殊的“多文件模式”;您只需不断添加 file 类型的 parameter,直到覆盖了该接口预期的所有部分。
将请求转化为可重复的测试场景
单次发送只能证明接口在当下是可用的。为了捕获回归问题,您需要将上传操作放入一个保存的测试场景中,以便按需或定时运行。串联这些步骤:上传头像,捕获返回的 id,然后调用 GET /users/{id} 并断言头像 URL 已持久化。
按照构建单个请求的相同方式构建它,然后将其保存为场景中的一个步骤。关于如何在 Apifox 中编写测试场景的指南介绍了步骤串联以及在步骤之间传递值的方法。一旦上传操作融入到场景中,您就可以在每次部署时针对 staging 环境运行它,在 API 测试场景中使用条件逻辑添加条件分支,或者通过定时 API 测试为其设置定时器。
以上所有内容在您的本地机器上都能正常运行,因为您的机器上存有该文件。但这个前提 nav precisamente 是接下来会导致问题的地方。
潜在的陷阱:在其他地方运行的上传
这就是“顺畅流程(happy path)”所掩盖的问题。Apifox 存储的是文件路径,而不是文件本身。在您的笔记本电脑上,这个问题并不会暴露,因为该路径每次都能解析到真实的文件。但一旦相同的步骤在不同的机器上运行,该路径就会指向空处。
您会在以下两种场景中遇到这个问题。
团队协作。 当团队成员打开您的 POST /avatars 请求时,他们会看到文件 parameter 以及您选择的路径(例如 /Users/jane/pics/jane-profile.png)。他们可以看到该请求,但无法发送,因为该文件存在于您的磁盘上,而不是他们的磁盘上。该路径仅在选择它的那台机器本地有效。
Runner 和 CLI 运行。 这是自动化测试中最容易遇到的坑。您的上传场景在本地通过了,但在 Runner 中配置了定时任务或通过 CLI 触发它时,文件上传步骤却失败了。您的断言没有任何问题,只是 runner 无法在您的笔记本电脑保存的路径下找到文件,因为该路径在运行 runner 的主机上并不存在。
解决方案取决于原因。文件必须存在于执行发送操作的机器上,并且步骤中的路径必须指向该文件在机器上的实际路径。
对于 Runner: Runner 会从挂载到其数据卷中的宿主机目录读取文件。你在部署 Runner 时,通过 -v 标志来设置该挂载。将你需要上传的文件复制到该已挂载的宿主机目录中。然后,在测试场景中打开该文件上传步骤的详情,点击右上角的 批量编辑 按钮,将文件字段的值替换为 Runner 目录内的路径,例如:
/opt/runner/jane-profile.png对于 CLI: 方法类似。将文件放在运行 CLI 的机器上,然后对该步骤使用 批量编辑,将路径指向该文件在本地的位置,例如:
/opt/apifox/runner/jane-profile.png比硬编码更优雅的做法:使用变量。 与其在步骤中写死字面量路径,不如将该值替换为变量,并根据不同的环境将变量的值设置为实际的文件路径。这样,同一个测试场景就可以直接在你的笔记本电脑、Runner 和 CI 上运行,而无需每次都去修改步骤。你可以将本地环境的变量值指向 /Users/jane/pics/jane-profile.png,而将 Runner 环境的变量值指向 /opt/runner/jane-profile.png,步骤本身则无需进行任何更改。
需要明确指出的一点是:Runner 只能访问你在部署时通过 -v 挂载的宿主机目录下的文件。如果你的文件不在该挂载目录下,任何路径都无法找到它。这属于部署配置层面的细节,而非方案本身的限制。如果你想查看官方权威版本,关于接口请求中上传文件的 Apifox 文档 详细说明了挂载和批量编辑的步骤。
使用 Apifox CLI 自动化工作流
一旦保存了上传测试场景,你就可以在 CI 中以无头(headless)模式运行它。安装 CLI 并进行身份验证:
npm install -g apifox-cli apifox login --with-token然后通过 ID 运行保存的测试场景,并将其指向某个环境:
apifox run --access-token $APIFOXACCESSTOKEN -t-e-r cli 这里 -t 是测试场景 ID,-e 是环境 ID,-r 是报告器类型(可使用 cli、html 或 junit,若有多个可用逗号分隔)。CLI 将运行你保存在云端项目中的测试场景,并通过退出代码报告通过/失败,从而实现对流水线的卡点控制。具体的配置细节请参考 Apifox CLI 安装指南。
需要特别注意的一点(与上一节提到的相同):包含文件上传步骤的测试场景需要文件存在于运行 CLI 的机器上,且步骤中的路径必须指向该文件在机器上的实际路径。在运行之前,需将文件放置在运行机器上,然后 批量编辑 路径(或使用变量)。如果忽略这一步,即使测试场景的其他部分都正常,上传步骤也会因找不到文件而失败。若要了解更完整的 CI 配置(包括传递逐行输入数据),请参阅使用 Apifox CLI 进行数据驱动测试。
常见问题
为什么我的队友无法发送我上传文件的请求? Apifox 存储的是本地文件路径,而不是文件本身,并且它绝不会将文件上传到云端。你的队友可以看到该请求以及你选择的路径,但该路径指向的是你磁盘上的文件,而不是他们的。让他们在自己的电脑上保存一份该文件的副本,并将该字段指向他们自己的路径。同样的原理也解释了为什么定时测试和 Runner 任务需要将文件存放在运行它们的环境中。
如何在同一个请求中同时发送 JSON 和文件? 将 body 类型保持为 form-data。添加一个类型为 file 的文件字段,然后添加另一个类型为 string 的 parameter,并将 JSON 粘贴到其值中。服务端会在一个 multipart 请求中接收到这两个部分:一部分是文件,另一部分是 JSON 字符串。这是为上传操作附加元数据的标准方式。
在 Runner 中我应该为文件使用什么路径? 使用部署时通过 -v 标志挂载到 Runner 卷中的宿主机目录内的路径,例如 /opt/runner/yourfile.jpg。将文件复制到该挂载目录中,然后打开步骤,点击 Batch Edit,并将该字段的值设置为该路径。对应的 CLI 路径类似于 /opt/apifox/runner/yourfile.jpg。
是否有文件大小限制或允许的文件类型列表? Apifox 中的上传行为仅关系到请求是如何构建的以及从何处读取文件。你实际的大小和类型限制取决于你正在测试的 API,因此请检查你服务端的校验规则,并针对其对超大文件或被拒绝文件返回的响应编写断言。
我应该使用 form-data 还是 x-www-form-urlencoded 进行上传? 使用 form-data。它映射到 multipart/form-data,专为携带文件而设计。而 x-www-form-urlencoded 用于不包含文件的简单表单(如简短的标量字段),因此它无法携带你的图片或 PDF 文件。
总结
文件上传测试归结为两点:正确构建 multipart 请求,并确保在运行测试的任何地方都可以访问到该文件。在 Apifox 中,将 body 设置为 form-data,将字段类型切换为 file,点击 Upload,将任何 JSON 作为字符串部分添加,然后发送并进行断言。当你将相同的场景移至 Runner 或 CLI 时,请将文件存放在该机器上,并使用 Batch Edit 或变量重新指向该路径,这样自动化运行的效果就会与本地运行一致。
想要针对你自己的接口进行尝试吗?下载 Apifox,将 form-data 请求指向你的上传路由,然后观察返回的响应。免费即可开始使用,无需信用卡。
开发必备:API 全流程管理神器 Apifox
介绍完上文的内容,我想额外介绍一个对开发者同样重要的效率工具 —— Apifox。作为一个集 API 文档、调试、设计、测试、Mock、自动化测试于一体的工具,Apifox 是目前提升研发效率的首选。
如果你正在开发项目,不妨试试其极其友好的界面设计,它完全兼容 Postman 和 Swagger 数据格式,导入数据非常方便,,即使是新手也能很快上手,点击这里即可注册使用。

值得一提的是,除了个人和常规团队使用,针对有高安全合规要求、或需要在内网环境协作的企业,Apifox 还提供了深度定制的私有化部署方案。
获取专属报价与部署方案
详细的私有化部署系统架构与安全白皮书
针对您公司规模的专属报价单
免费的 1v1 专属产品演示 (Demo) 机会