ab 测试时,发送 multipart form-data 的方法及数据的构造

ab 使用 post 提交数据,而有些数据是以 multipart/form-data 形式提交的,而在 ab 中,
要提交数据,一般使用的格式是

1
ab -v 2 -c 10 -n 1000 -T "multipart/form-data; boundary=----WebKitFormBoundaryxgAgRcQ0HRz5wrwN" -p data.txt http://192.168.1.191:9889/api/image/upload

像上面那样,指定了 T 参数,同时使用了 p 参数指定了要上传的文件内容。

这里复杂的地方就在于 data.txt 的内容构造,它的内部构造要符合 rfc1867 协议的规范。
我耗费了半个晚上,不停地搜索和测试,终于弄清楚了如何构造一个符合条件的上传数据。

这里以上传一张图片为例,下面使用了 python 代码来辅助说明,不过对于协议来说,用什么语言都无所谓。

首先,这个上传数据由三部分组成:

  • 边界
  • 头部信息
  • 主体数据

具体构造信息的方法

边界

这里怎么说呢?为了区分我们上传的数据的起始位置,我们需要定义一个上线边界,用来告诉服务器被这两行夹着的数据就是上传的数据。
边界值可以随便设置,但是为了不和数据内容冲突,最好的是设置为比较复杂的值,比如默认的 chrome 上传文件时,边界值就是类似于

------WebKitFormBoundaryxgAgRcQ0HRz5wrwN 这样的。

边界要特别注意的一点是,写在数据文件中的边界值和上面 ab 命令中的边界值相比,在起始点,多了两个短横线,在结束点,头尾都多了两个
短横线。用一个例子来说吧。

假设有命令的部分是 ab -T "multipart/form-data; boundary=----WebKitFormBoundaryxgAgRcQ0HRz5wrwN" -p data.txt 这里,边界值是
----WebKitFormBoundaryxgAgRcQ0HRz5wrwN,那么在数据文件 data.txt 中,首行就是 ------WebKitFormBoundaryxgAgRcQ0HRz5wrwN
最后一行就是 ------WebKitFormBoundaryxgAgRcQ0HRz5wrwN--,这里是构造 data.txt 数据文件时,第一个要注意的地方。

头部信息

头部信息主要由两部分组成,包含 Content-DispositionContent-Type,这里在 Content-Disposition 要指明数据格式、服务端接收文件时的
名字、以及传递的文件的名字,下面是一个例子:

1
Content-Disposition: form-data; name="upimg"; filename="2015-08-20-23-16-57.png"

而在 Content-Type 中,一般就是指明MIME类型,比如说这里是:

1
Content-Type: image/png

主体数据

主体数据就是真实的文件数据的 base64 编码后的数据,这里特别要注意是,将文件按照二进制读取,然后进行 base64 编码,就得到了要传的文件的编码数据,
下面用 python 代码做了个简单的例子。

1
2
3
4
5
6
import base64
filename = "mypicture.png"
with open(filename, 'rb') as f:
raw_data = f.read()
b64data = base64.b64encode(raw_data)

构造 data.txt 文件

这里有两个特别要注意的地方是:

  • 换行使用 \r\n
  • 头部信息结束使用 \r\n\r\n

下面具体列一个拼接 data.txt 的例子

1
2
3
4
5
------WebKitFormBoundaryxgAgRcQ0HRz5wrwN\r\n
Content-Disposition: form-data; name="upimg"; filename="2015-08-20-23-16-57.png"\r\n
Content-Type: image/png\r\n\r\n
xxxxx(here is base64 encode data) \r\n
------WebKitFormBoundaryxgAgRcQ0HRz5wrwN--\r\n

上面就构造了一个符合规范的数据文件。

特别要注意的两件事

第一件事是,边界值,在 ab 中的边界值比数据文件中的边界值在字符串的起始位置开始,少了两个短横线;

第二件事是,每一行数据的换行符都是 \r\n,而头部信息结束时是 \r\n\r\n

其他一般没有什么了。

我用 python 写了个快速转换一个文件为 ab 可用的数据文件的程序,放在了 github.com 上。