JMeter 源码解析之一 JMeter 上传文件时,如何参数化 Content-Disposition 的 filen

本文介绍了在JMeter中如何处理上传文件时Content-Disposition的参数化问题,分别从添加HTTP参数、设置HTTP信息头管理器以及使用BeanShell三种角度提供了详细解决方案,涵盖了Java、HttpClient4和HttpClient3.1三种Implementation的实现方式。
               

        问题描述

        文件上传时,用户定义 Content-Disposition 是失效的。
        笔者在写压力测试脚本的时候,有个上传页面,服务器是根据用户传过来的 Content-Disposition 里的 filename 值来定义保存文件的文件名的。但是测试人员不可能为每一次请求都准备一个不同的文件(这个工作量海了去了),所以 JMeter 传给服务器的 Content-Disposition 里的 filename 必须是随机而不重复的。
        有人问,用户真实上传时,浏览器传给服务器的 filename 也是上传文件名吗?不是的,js 这样修改的 filename:
            uploader.onBeforeUploadItem = function (item) {                //修改名字                var timeStamp = new Date().getTime();                var fileName = item.file.name;                item.file.name = timeStamp + fileName.substr(fileName.lastIndexOf('.'));                var day = $filter('date')(new Date(), 'yyyyMMdd');                item.url = [item.url, "batchImport", item.importType, day, session.userId].join("/");            };

        笔者尝试了多种办法,试图修改服务器接收到的 filename 值,结果都失败了。笔者尝试的办法有:

        1. 添加 HTTP 参数

添加 HTTP 参数
        如图所示,我们期待服务器接收到的 filename 值是 00004000.xls,而不是 00000000.xls。
        结果服务器接收到的是 00000000.xls。服务器返回给客户端的存储路径为证:/batchImport/merAdd/20141128/1/00000000.xls。查看本次 HTTP 请求,可以看到以下信息:
POST http://serverIP/upload/batchImport/merAdd/20141128/1

POST data:
--DoZtX5jrOIxJTocysPzYJ1WVqtoagXMQHHqho4i
Content-Disposition: form-data; name="Content-Disposition"
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 8bit

form-data; name="14170058206940.xls"; filename="00004000.xls"
--DoZtX5jrOIxJTocysPzYJ1WVqtoagXMQHHqho4i
Content-Disposition: form-data; name="file"; filename=" 00000000.xls"
Content-Type: application/vnd.ms-excel
Content-Transfer-Encoding: binary

<actual file content, not shown here>
--DoZtX5jrOIxJTocysPzYJ1WVqtoagXMQHHqho4i--

Cookie Data:
$Version=0; JSESSIONID=AC79777AEFE5AFC690623FCCB09E5DD5; $Path=/

Request Headers:
Connection: keep-alive
Content-Length: 34786
Content-Type: multipart/form-data; boundary=DoZtX5jrOIxJTocysPzYJ1WVqtoagXMQHHqho4i
Host: serverIP
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
        看来 JMeter 把我们的 Content-Disposition 参数名字都丢了。

        2. 添加 HTTP 信息头管理器

添加 HTTP 信息头管理器
        如图所示,我们期待服务器接收到的 filename 值是 40004000.xls,而不是 00000000.xls。
        然后我们发次请求,然后查看本次 HTTP 请求,可以看到以下信息:
POST http://serverIP/upload/batchImport/merAdd/20141128/1

POST data:
--BNKvCNweqwpTJToYINcDn6JJfzjazBE550a-
Content-Disposition: form-data; name="file"; filename=" 00000000.xls"
Content-Type: application/vnd.ms-excel
Content-Transfer-Encoding: binary

<actual file content, not shown here>
--BNKvCNweqwpTJToYINcDn6JJfzjazBE550a---

Cookie Data:
$Version=0; JSESSIONID=49AB53310FB7241B5544B4E747A58F80; $Path=/

Request Headers:
Connection: keep-alive
Content-Disposition: form-data; name="file"; filename="40004000.xls"
Content-Length: 34535
Content-Type: multipart/form-data; boundary=BNKvCNweqwpTJToYINcDn6JJfzjazBE550a-
Host: serverIP
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
        这次 JMeter 没有把我们的 Content-Disposition 弄丢,它出现在了 Request Headers 里边。但是服务器貌似读取的是 POST data 中 Content-Disposition 里的那个 filename。有服务器返回给客户端的存储路径为证:/batchImport/merAdd/20141128/1/ 00000000.xls

        3. 使用 BeanShell

BeanShell
        如图所示,我们期待服务器接收到的 filename 值是 40004004.xls,而不是 00000000.xls。我们怀着期待的心情再次向服务器发起请求。请求如下:
POST http://serverIP/upload/batchImport/merAdd/20141128/1

POST data:
--LWS2eUVPPPuDxcfT7dS4RpqQJe2uP_0lAme6Qx2Q
Content-Disposition: form-data; name="file"; filename=" 00000000.xls"
Content-Type: application/vnd.ms-excel
Content-Transfer-Encoding: binary

<actual file content, not shown here>
--LWS2eUVPPPuDxcfT7dS4RpqQJe2uP_0lAme6Qx2Q--

Cookie Data:
$Version=0; JSESSIONID=81514F48024CE0B4CB53DB0CBC283C11; $Path=/

Request Headers:
Connection: keep-alive
Content-Length: 34543
Content-Type: multipart/form-data; boundary=LWS2eUVPPPuDxcfT7dS4RpqQJe2uP_0lAme6Qx2Q
Host: serverIP
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
        结果是不管是前置 BeanShell,还是 BeanShell 监听器,显然对于我们的需求无能为力。
        向万能的谷歌求助,得到的结果基本都是 It's impossible。
        最后笔者怀着郁闷的心情去找这个项目的责任人,试图说服他,服务器不应该以客户端传来的 filename 对保存文件进行命名,应该有自己的一套随机生成文件名的规则,得到的答复却是:NO。
        万般无奈之下,笔者只好去看 JMeter 的源代码了。好嘛,JMeter 2.12 的源代码(src 目录下的纯 *.java 文件)足足有 6.75 MB。而且还是用 Ant 代码管理的,黑压压的看着森人。哎,不爽也得看,没办法啊,谁让咱要吃性能测试这碗饭呢,工作总是要继续的吧。
        硬着头皮看了一下午,结果很不幸,发现 JMeter 是把 Content-Disposition 里的 filename 写死的,它压根儿就没想留给用户对  filename 进行参数化途径!
        比如 org.apache.jmeter.protocol.http.sampler.PostWriter 的 writeStartFileMultipart 方法是这样写死的:
    /**     * Write the start of a file multipart, up to the point where the     * actual file content should be written     */    private void writeStartFileMultipart(OutputStream out, String filename,            String nameField, String mimetype)      &n
jmeter上传文件的接口,请求是 POST https://metapta.migu.cn/bit-id/3d-digital/avatar-api/v1/knowledge/file/upload POST data: --5OS0qoUAix1Lz4aCWFK6XKv19tsBqCPuCmH_ Content-Disposition: form-data; name="knowledgeId" Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1498935310975762549 --5OS0qoUAix1Lz4aCWFK6XKv19tsBqCPuCmH_ Content-Disposition: form-data; name="srcFile"; filename="?????????? ???????? ?????????????.docx" Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document Content-Transfer-Encoding: binary <actual file content, not shown here> --5OS0qoUAix1Lz4aCWFK6XKv19tsBqCPuCmH_-- Cookie Data: Admin-Mobile=13430966064; Admin-Token=" Bearer eyJhbGciOiJIUzI1NiJ9.eyJ0eXBlIjoid2ViIiwiYWNjb3VudCI6IjEzNDMwOTY2MDY0Iiwic3ViIjoiMTk3OTUyMTA3NzU4MjY3NTk2OSIsImlzcyI6ImF2YXRhci11c2VyLXNlcnZlciIsImlhdCI6MTc2MDkzOTA2NywiZXhwIjoxNzYxNTQzODY3LCJuYmYiOjE3NjA5MzkwNjd9.CKRDwqWVTxhALcIkZDWiO1xpSkuez230AHLU2YPxu-A"响应是 java.io.FileNotFoundException: C:\Users\EDY\Desktop\shuzhirenxiangmu\wodezhishiku\jituanxinxi\jituanxinxi\���������� �ұ���Ӧ�� �й��ƶ�֣�س�ŵ��.docx (系统找不到指定的路径。) at java.base/java.io.FileInputStream.open0(Native Method) at java.base/java.io.FileInputStream.open(FileInputStream.java:216) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157) at org.apache.http.entity.mime.content.FileBody.writeTo(FileBody.java:116) at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl$ViewableFileBody.writeTo(HTTPHC4Impl.java:1513) at org.apache.http.entity.mime.AbstractMultipartForm.doWriteTo(AbstractMultipartForm.java:134) at org.apache.http.entity.mime.AbstractMultipartForm.writeTo(AbstractMultipartForm.java:157) at org.apache.http.entity.mime.MultipartFormEntity.writeTo(MultipartFormEntity.java:113) at org.apache.http.impl.DefaultBHttpClientConnection.sendRequestEntity(DefaultBHttpClientConnection.java:156) at org.apache.http.impl.conn.CPoolProxy.sendRequestEntity(CPoolProxy.java:152) at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:238) at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl$2.doSendRequest(HTTPHC4Impl.java:458) at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83) at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.executeRequest(HTTPHC4Impl.java:935) at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:646) at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:66) at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1296) at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1285) at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:638) at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:558) at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:489) at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:256) at java.base/java.lang.Thread.run(Thread.java:833)
10-28
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值