123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- [[multipart]]
- == Multipart requests
- Multipart originates from MIME, an Internet standard that
- extends the format of emails.
- A multipart message is a list of parts. A part contains
- headers and a body. The body of the parts may be
- of any media type, and contain text or binary data.
- It is possible for parts to contain a multipart media
- type.
- In the context of HTTP, multipart is most often used
- with the `multipart/form-data` media type. It is what
- browsers use to upload files through HTML forms.
- The `multipart/byteranges` is also common. It is the
- media type used to send arbitrary bytes from a resource,
- enabling clients to resume downloads.
- === Form-data
- In the normal case, when a form is submitted, the
- browser will use the `application/x-www-form-urlencoded`
- content-type. This type is just a list of keys and
- values and is therefore not fit for uploading files.
- That's where the `multipart/form-data` content-type
- comes in. When the form is configured to use this
- content-type, the browser will create a multipart
- message where each part corresponds to a field on
- the form. For files, it also adds some metadata in
- the part headers, like the file name.
- A form with a text input, a file input and a select
- choice box will result in a multipart message with
- three parts, one for each field.
- The browser does its best to determine the media type
- of the files it sends this way, but you should not
- rely on it for determining the contents of the file.
- Proper investigation of the contents is recommended.
- === Checking for multipart messages
- The content-type header indicates the presence of
- a multipart message:
- [source,erlang]
- ----
- {<<"multipart">>, <<"form-data">>, _}
- = cowboy_req:parse_header(<<"content-type">>, Req).
- ----
- === Reading a multipart message
- Cowboy provides two sets of functions for reading
- request bodies as multipart messages.
- The `cowboy_req:read_part/1,2` functions return the
- next part's headers, if any.
- The `cowboy_req:read_part_body/1,2` functions return
- the current part's body. For large bodies you may
- need to call the function multiple times.
- To read a multipart message you need to iterate over
- all its parts:
- [source,erlang]
- ----
- 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.
- ----
- When part bodies are too large, Cowboy will return
- a `more` tuple, and allow you to loop until the part
- body has been fully read.
- The function `cow_multipart:form_data/1` can be used
- to quickly obtain information about a part from a
- `multipart/form-data` message. The function returns
- a `data` or a `file` tuple depending on whether this
- is a normal field or a file being uploaded.
- The following snippet will use this function and
- use different strategies depending on whether the
- part is a file:
- [source,erlang]
- ----
- 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, _CTransferEncoding} ->
- stream_file(Req1)
- end,
- multipart(Req);
- {done, Req} ->
- Req
- end.
- stream_file(Req0) ->
- case cowboy_req:read_part_body(Req0) of
- {ok, _Body, Req} ->
- Req;
- {more, _Body, Req} ->
- stream_file(Req)
- end.
- ----
- Both the part header and body reading functions can take
- options that will be given to the request body reading
- functions. By default, `cowboy_req:read_part/1` reads
- up to 64KB for up to 5 seconds. `cowboy_req:read_part_body/1`
- has the same defaults as `cowboy_req:read_body/1`.
- To change the defaults for part headers:
- [source,erlang]
- cowboy_req:read_part(Req, #{length => 128000}).
- And for part bodies:
- [source,erlang]
- cowboy_req:read_part_body(Req, #{length => 1000000, period => 7000}).
- === Skipping unwanted parts
- Part bodies do not have to be read. Cowboy will automatically
- skip it when you request the next part's body.
- The following snippet reads all part headers and skips
- all bodies:
- [source,erlang]
- ----
- multipart(Req0) ->
- case cowboy_req:read_part(Req0) of
- {ok, _Headers, Req} ->
- multipart(Req);
- {done, Req} ->
- Req
- end.
- ----
- Similarly, if you start reading the body and it ends up
- being too big, you can simply continue with the next part.
- Cowboy will automatically skip what remains.
- While Cowboy can skip part bodies automatically, the read
- rate is not configurable. Depending on your application
- you may want to skip manually, in particular if you observe
- poor performance while skipping.
- You do not have to read all parts either. You can stop
- reading as soon as you find the data you need.
- // @todo Cover the building of multipart messages.
|