Multipart(多部件)源自MIME, MIME是一种扩展电子邮件格式的互联网标准。
multipart消息是一个部件列表。一个部件包含报头和主体。部件的主体可以是任何媒体类型,并包含文本或二进制数据。部件可以包含多部件媒体类型。
在HTTP上下文中,multipart最常与multipart/form-data媒体类型一起使用。浏览器使用它通过HTML表单上传文件。
multipart/byteranges
也很常见。它是用于从资源发送任意字节的媒体类型,使客户端能够恢复下载。
form-data)
通常情况下,当提交表单时,浏览器将使用application/x-www-form-urlencoded content-type。这种类型只是一个键和值的列表,因此不适合上传文件。
这就是多部件/表单数据(multipart/form-data
) content-type的作用所在。当将表单配置为使用此内容类型时,浏览器将创建一个多部件消息,其中每个部分对应于表单上的一个字段。对于文件,它还在部件头中添加一些元数据,如文件名。
带有文本输入、文件输入和选择选择框的表单将生成包含三个部分的多部分消息,每个字段一个部分。
浏览器尽其所能确定它通过这种方式发送的文件的媒体类型,但您不应该依赖它来确定文件的内容。建议对内容进行适当的调查。
content-type报头表示多部件消息的存在:
{<<"multipart">>, <<"form-data">>, _} = cowboy_req:parse_header(<<"content-type">>, Req).
Cowboy提供了两组函数,用于将请求正文作为多部分消息读取。
cowboy_req:read_part/1,2函数返回下一部分的报头信息,如果有的话。
read_part_body/1,2函数返回当前部件的主体。对于较大的函数体,可能需要多次调用该函数。
要读取一个多部分消息,你需要遍历它的所有部分:
multipart(Req0) -> case cowboy_req:read_part(Req0) of {ok, _Headers, Req1} -> {ok, _Body, Req} = cowboy_req:read_part_body(Req1), multipart(Req); {done, Req} -> Req end.
当部分主体太大时,Cowboy将返回一个更大的元组,并允许您循环,直到部分主体被完全读取。
函数cow_multipart:form_data/1可用于从 多部件/表单数据(multipart/form-data
) 消息中快速获取部件信息。该函数返回一个数据或文件元组,取决于这是一个普通字段还是一个正在上传的文件。
以下代码片段将使用此函数,并根据是否为文件使用不同的策略:
multipart(Req0) -> case cowboy_req:read_part(Req0) of {ok, Headers, Req1} -> Req = case cow_multipart:form_data(Headers) of {data, _FieldName} -> {ok, _Body, Req2} = cowboy_req:read_part_body(Req1), Req2; {file, _FieldName, _Filename, _CType} -> stream_file(Req1) end, multipart(Req); {done, Req} -> Req end. stream_file(Req0) -> case cowboy_req:read_part_body(Req0) of {ok, _LastBodyChunk, Req} -> Req; {more, _BodyChunk, Req} -> stream_file(Req) end.
部件报头和主体读取函数都可以接受将提供给请求正文读取函数的选项。默认情况下,cowboy_req:read_part/1最多读取64KB,持续5秒。cowboy_req:read_part_body/1的默认值与cowboy_req:read_body/1相同。
更改部件报头的默认值:
cowboy_req:read_part(Req, #{length => 128000}).
对于部分主体:
cowboy_req:read_part_body(Req, #{length => 1000000, period => 7000}).
部分主体不需要读。当你要求下一个部件的主体时Cowboy会自动跳过。
以下代码片段读取所有部分的报头,并跳过所有主体:
multipart(Req0) -> case cowboy_req:read_part(Req0) of {ok, _Headers, Req} -> multipart(Req); {done, Req} -> Req end.
类似地,如果你开始阅读主体,最后发现它太大了,你可以继续读下一部件。Cowboy会自动跳过剩下的部分。
虽然Cowboy可以自动跳过部分正文,但读取速率是不可配置的。根据您的应用程序,您可能希望手动跳过,特别是在跳过时发现性能很差的情况下。
你也不必把所有部件都读一遍。一旦你找到你需要的数据,你就可以停止阅读。