在C# Web API开发中,文件上传是非常常见的业务场景,但很多接口在功能测试阶段表现正常,一到生产环境多用户同时传大文件就出现响应缓慢甚至服务崩溃。要提前发现这类隐患,就必须对上传API做负载测试。本文以ASP.NET Core的文件上传接口为例,讲解如何使用k6和JMeter两种工具进行压力测试。

一、准备C#文件上传测试接口
我们先在ASP.NET Core中写一个最简单的文件上传Action,用于后续压测。该接口接收表单中的文件,并保存到本地磁盘。注意这里故意没有做太多限制,方便观察压力下的表现。
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Threading.Tasks;
[ApiController]
[Route("api/upload")]
public class UploadController : ControllerBase
{
// 简单的文件上传接口,仅用于压力测试演示
[HttpPost]
public async Task<IActionResult> Post()
{
var file = Request.Form.Files["file"];
if (file == null || file.Length == 0)
{
return BadRequest("未接收到文件");
}
var savePath = Path.Combine(Directory.GetCurrentDirectory(), "uploads", file.FileName);
using (var stream = new FileStream(savePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return Ok(new { length = file.Length });
}
}
上面的代码直接把整个文件CopyToAsync到磁盘,在高并发时会造成大量内存和IO争用。实际项目中应该配合RequestSizeLimit和分块读取来优化,但作为压测对象已经足够。
启动该接口后,假设监听地址为 http://127.0.0.1:5000 ,我们就有了被测目标。接下来分别用k6和JMeter模拟多用户上传。
二、使用k6进行文件上传压力测试
k6是一款基于JavaScript的现代负载测试工具,适合开发人员写代码化脚本。它原生支持multipart表单,可以方便地上传文件。我们首先要准备一个测试用的小文件,比如test.txt。
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
vus: 50,
duration: '30s',
};
export default function () {
// 读取本地文件并以multipart形式上传
const fileData = open('test.txt');
const url = 'http://127.0.0.1:5000/api/upload';
const payload = {
file: http.file(fileData, 'test.txt', 'text/plain'),
};
const res = http.post(url, payload);
check(res, {
'status is 200': (r) => r.status === 200,
});
sleep(1);
}
将上述脚本保存为script.js,并在同目录放置test.txt,执行k6 run script.js即可启动50个虚拟用户持续30秒的上传测试。k6会以命令行实时输出TPS、请求耗时和错误率。
如果test.txt太小,无法体现大文件压力,可以换用几MB的样张。k6的优势在于脚本可纳入版本库,和C#项目一起做自动化压测流水线;缺点是图形化报告弱,需要自己导出的JSON做分析。
三、使用JMeter进行文件上传压力测试
JMeter是老牌GUI压测工具,对非开发同学更友好。我们新建一个测试计划,添加线程组,设置线程数50、循环次数适当,再添加HTTP请求采样器。
在HTTP请求中,方法选POST,路径填 http://127.0.0.1:5000/api/upload ,勾选“对POST使用multipart/form-data”。在“文件上传”标签页里,文件名称填本地绝对路径,参数名称填file,MIME类型填text/plain。这样就完成了上传配置。
<!-- JMeter jmx片段示例,仅展示HTTP请求核心配置 -->
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy">
<stringProp name="HTTPSampler.path">/api/upload</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.postBodyRaw">false</boolProp>
<elementProp name="HTTPsampler.Files" elementType="HTTPFileArgs">
<collectionProp name="HTTPFileArgs.files">
<elementProp name="" elementType="HTTPFileArg">
<stringProp name="File.path">D:/test.txt</stringProp>
<stringProp name="File.paramname">file</stringProp>
<stringProp name="File.mimetype">text/plain</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
运行后通过聚合报告能看到平均、中位数、90线响应时间。JMeter自带图形界面和丰富监听器,便于现场调参;但GUI本身耗资源,做高并发建议用命令行模式jmeter -n -t plan.jmx -l result.jtl。
对比来看,k6脚本轻量、易集成CI;JMeter上手快、报表直观。团队可根据成员技能选择,核心都是构造正确的multipart请求命中C#接口。
四、压测中常见C#服务端问题
在压力测试中,C#上传API最容易暴露的是内存和线程瓶颈。由于示例里用了CopyToAsync整体拷贝,IIS或Kestrel的工作线程会被大文件IO占满,导致后续请求排队。
优化方向之一是限制请求大小,在Action或Startup里配置[RequestSizeLimit(10_000_000)],拒绝超大文件。其二是使用分块流式处理,不把整个文件加载进内存。还可在Program.cs中调整Kestrel的吞吐限制。压测时观察dotnet进程的CPU和GC频率,就能判断瓶颈在编解码还是磁盘。
| 观察指标 | 可能问题 | 优化手段 |
|---|---|---|
| GC Gen2频繁 | 大文件字节数组常驻 | 改用Stream分块写 |
| 线程池排队 | 同步IO阻塞 | 全链路async/await |
| 磁盘写入慢 | 并发落盘争用 | 临时缓冲或对象存储 |
通过k6或JMeter反复加压,就能得到接口在多少并发下开始报错,从而定出SLA。这样上生产前心里才有底。
五、总结与实践建议
文件上传API的负载测试不能只靠功能用例。用k6写几行脚本或JMeter画几个组件,都可以真实模拟用户批量传文件。建议把压测脚本和C#代码放同一个仓库,每次发布前自动跑一轮。
当后台监控显示错误率陡增时,优先查服务端日志中的BadHttpRequestException和磁盘剩余空间。只要压测覆盖到正常峰值的一点五倍流量,系统稳定性就会有明显提升。
C#文件上传k6压力测试JMeter上传测试修改时间:2026-08-07 10:54:35