What does enctype='multipart/form-data' mean in an HTML form and when should we use it?
When you make a POST request, you have to encode the data that forms the body of the request in some way.
HTML forms provide three methods of encoding.
application/x-www-form-urlencoded (the default)
multipart/form-data
text/plain
Work was being done on adding application/json, but that has been abandoned.
(Other encodings are possible with HTTP requests generated using other means than an HTML form submission. JSON is a common format for use with web services and some still use SOAP.)
The specifics of the formats don't matter to most developers. The important points are:
Never use text/plain.
When you are writing client-side code:
use multipart/form-data when your form includes any <input type="file"> elements
otherwise you can use multipart/form-data or application/x-www-form-urlencoded but application/x-www-form-urlencoded will be more efficient
When you are writing server-side code:
Use a prewritten form handling library
Most (such as Perl's CGI->param or the one exposed by PHP's $_POST superglobal) will take care of the differences for you. Don't bother trying to parse the raw input received by the server.
Sometimes you will find a library that can't handle both formats. Node.js's most popular library for handling form data is body-parser which cannot handle multipart requests (but has documentation that recommends some alternatives which can).
If you are writing (or debugging) a library for parsing or generating the raw data, then you need to start worrying about the format. You might also want to know about it for interest's sake.
application/x-www-form-urlencoded is more or less the same as a query string on the end of the URL.
multipart/form-data is significantly more complicated but it allows entire files to be included in the data. An example of the result can be found in the HTML 4 specification.
text/plain is introduced by HTML 5 and is useful only for debugging — from the spec: They are not reliably interpretable by computer — and I'd argue that the others combined with tools (like the Network Panel in the developer tools of most browsers) are better for that).
when should we use it?
Quentin's answer is right: use multipart/form-data if the form contains a file upload, and application/x-www-form-urlencoded otherwise, which is the default if you omit enctype.
I'm going to:
add some more HTML5 references
explain why he is right with a form submit example
HTML5 references
There are three possibilities for enctype:
application/x-www-form-urlencoded
multipart/form-data (spec points to RFC7578)
text/plain. This is "not reliably interpretable by computer", so it should never be used in production, and we will not look further into it.
How to generate the examples
Once you see an example of each method, it becomes obvious how they work, and when you should use each one.
You can produce examples using:
nc -l or an ECHO server: HTTP test server accepting GET/POST requests
a user agent like a browser or cURL
Save the form to a minimal .html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>upload</title>
</head>
<body>
<form action="http://localhost:8000" method="post" enctype="multipart/form-data">
<p><input type="text" name="text1" value="text default">
<p><input type="text" name="text2" value="aωb">
<p><input type="file" name="file1">
<p><input type="file" name="file2">
<p><input type="file" name="file3">
<p><button type="submit">Submit</button>
</form>
</body>
</html>
We set the default text value to aωb, which means aωb because ω is U+03C9, which are the bytes 61 CF 89 62 in UTF-8.
Create files to upload:
echo 'Content of a.txt.' > a.txt
echo '<!DOCTYPE html><title>Content of a.html.</title>' > a.html
# Binary file containing 4 bytes: 'a', 1, 2 and 'b'.
printf 'a\xCF\x89b' > binary
Run our little echo server:
while true; do printf '' | nc -l localhost 8000; done
Open the HTML on your browser, select the files and click on submit and check the terminal.
nc prints the request received.
Tested on: Ubuntu 14.04.3, nc BSD 1.105, Firefox 40.
multipart/form-data
Firefox sent:
POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
Content-Length: 834
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text1"
text default
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text2"
aωb
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain
Content of a.txt.
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html
<!DOCTYPE html><title>Content of a.html.</title>
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file3"; filename="binary"
Content-Type: application/octet-stream
aωb
-----------------------------735323031399963166993862150--
For the binary file and text field, the bytes 61 CF 89 62 (aωb in UTF-8) are sent literally. You could verify that with nc -l localhost 8000 | hd, which says that the bytes:
61 CF 89 62
were sent (61 == 'a' and 62 == 'b').
Therefore it is clear that:
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150 sets the content type to multipart/form-data and says that the fields are separated by the given boundary string.
But note that the:
boundary=---------------------------735323031399963166993862150
has two less dashes -- than the actual barrier
-----------------------------735323031399963166993862150
This is because the standard requires the boundary to start with two dashes --. The other dashes appear to be just how Firefox chose to implement the arbitrary boundary. RFC 7578 clearly mentions that those two leading dashes -- are required:
4.1. "Boundary" Parameter of multipart/form-data
As with other multipart types, the parts are delimited with a
boundary delimiter, constructed using CRLF, "--", and the value of
the "boundary" parameter.
every field gets some sub headers before its data: Content-Disposition: form-data;, the field name, the filename, followed by the data.
The server reads the data until the next boundary string. The browser must choose a boundary that will not appear in any of the fields, so this is why the boundary may vary between requests.
Because we have the unique boundary, no encoding of the data is necessary: binary data is sent as is.
TODO: what is the optimal boundary size (log(N) I bet), and name / running time of the algorithm that finds it? Asked at: https://cs.stackexchange.com/questions/39687/find-the-shortest-sequence-that-is-not-a-sub-sequence-of-a-set-of-sequences
Content-Type is automatically determined by the browser.
How it is determined exactly was asked at: How is mime type of an uploaded file determined by browser?
application/x-www-form-urlencoded
Now change the enctype to application/x-www-form-urlencoded, reload the browser, and resubmit.
Firefox sent:
POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: application/x-www-form-urlencoded
Content-Length: 51
text1=text+default&text2=a%CF%89b&file1=a.txt&file2=a.html&file3=binary
Clearly the file data was not sent, only the basenames. So this cannot be used for files.
As for the text field, we see that usual printable characters like a and b were sent in one byte, while non-printable ones like 0xCF and 0x89 took up 3 bytes each: %CF%89!
Comparison
File uploads often contain lots of non-printable characters (e.g. images), while text forms almost never do.
From the examples we have seen that:
multipart/form-data: adds a few bytes of boundary overhead to the message, and must spend some time calculating it, but sends each byte in one byte.
application/x-www-form-urlencoded: has a single byte boundary per field (&), but adds a linear overhead factor of 3x for every non-printable character.
Therefore, even if we could send files with application/x-www-form-urlencoded, we wouldn't want to, because it is so inefficient.
But for printable characters found in text fields, it does not matter and generates less overhead, so we just use it.
enctype='multipart/form-data is an encoding type that allows files to be sent through a POST. Quite simply, without this encoding the files cannot be sent through POST.
If you want to allow a user to upload a file via a form, you must use this enctype.
When submitting a form, you tell your browser to send, via the HTTP protocol, a message on the network, properly enveloped in a TCP/IP protocol message structure. An HTML page has a way to send data to the server: by using <form>s.
When a form is submitted, an HTTP Request is created and sent to the server, the message will contain the field names in the form and the values filled in by the user. This transmission can happen with POST or GET HTTP methods.
POST tells your browser to build an HTTP message and put all content in the body of the message (a very useful way of doing things, more safe and also flexible).
GET will submit the form data in the querystring. It has some constraints about data representation and length.
Stating how to send your form to the server
Attribute enctype has sense only when using POST method. When specified, it instructs the browser to send the form by encoding its content in a specific way. From MDN - Form enctype:
When the value of the method attribute is post, enctype is the MIME
type of content that is used to submit the form to the server.
application/x-www-form-urlencoded: This is the default. When the form is sent, all names and values are collected and URL Encoding is performed on the final string.
multipart/form-data: Characters are NOT encoded. This is important when the form has a file upload control. You want to send the file binary and this ensures that bitstream is not altered.
text/plain: Spaces get converted, but no more encoding is performed.
Security
When submitting forms, some security concerns can arise as stated in RFC 7578 Section 7: Multipart form data - Security considerations:
All form-processing software should treat user supplied form-data
with sensitivity, as it often contains confidential or personally
identifying information. There is widespread use of form "auto-fill"
features in web browsers; these might be used to trick users to
unknowingly send confidential information when completing otherwise
innocuous tasks. multipart/form-data does not supply any features
for checking integrity, ensuring confidentiality, avoiding user
confusion, or other security features; those concerns must be
addressed by the form-filling and form-data-interpreting applications.
Applications that receive forms and process them must be careful
not to supply data back to the requesting form-processing site that
was not intended to be sent.
It is important when interpreting the filename of the Content-
Disposition header field to not inadvertently overwrite files in the
recipient's file space.
This concerns you if you are a developer and your server will process forms submitted by users which might end up containing sensitive information.
enctype='multipart/form-data' means that no characters will be encoded. that is why this type is used while uploading files to server.
So multipart/form-data is used when a form requires binary data, like the contents of a file, to be uploaded
Set the method attribute to POST because file content can't be put inside a URL parameter using a form.
Set the value of enctype to multipart/form-data because the data will be split into multiple parts, one for each file plus one for the text of the form body that may be sent with them.
enctype(ENCode TYPE) attribute specifies how the form-data should be encoded when submitting it to the server.
multipart/form-data is one of the value of enctype attribute, which is used in form element that have a file upload. multi-part means form data divides into multiple parts and send to server.
Usually this is when you have a POST form which needs to take a file upload as data... this will tell the server how it will encode the data transferred, in such case it won't get encoded because it will just transfer and upload the files to the server, Like for example when uploading an image or a pdf
The enctype attribute specifies how the form-data should be encoded when submitting it to the server.
The enctype attribute can be used only if method="post".
No characters are encoded. This value is required when you are using forms that have a file upload control
From W3Schools
Summary: I would like to make a Range header request from GitHub pages. However, in some browsers this is failing- possibly due to Gzip compression issues. It works in Chrome (v74) but not in FF (v66), Mac OS.
Goal: I would like to reliably make this request in all browsers, such as by forcing the response type to be encoded as text whenever a range request is made.
It is not clear to me whether this behavior is dictated by the browser, the server, or some combination of the two. Knowing origins could help to define a fix- working with Github pages would be nice but not mandatory.
It is also not clear to me whether this represents a bug, or if so, where. (in browser, in spec, etc)
Sample test case:
Possibly because this involves server-side gzip encoding, the sample test case doesn't reproduce locally. You'll need to enter these commands in the JS console at https://abought.github.io/weetabix/ to reproduce.
fetch('https://abought.github.io/weetabix/example_data/McDonald_s.csv', {headers: {range: 'bytes=1-100'}} ).then(resp => resp.text());
In chrome, this fetches the response text. In firefox, it gives a "decoding error".
If I omit resp.text, Firefox can complete the request- the decoding error is in reading the body, rather than any other code. Copying as curl shows that FF adds a --compress flag and Chrome does not.
Investigations
If the byte range is 0-100, the request works in FF. If the range is 1-100, it fails. This section of the file is all ASCII characters.
If I inspect the response headers (Array.from(r.headers.entries())), FF has an extra "content-encoding: gz flag" that I think is causing the issue.
(eg, gzip makes no sense without the secret decoder instructions)
I tried adding 'accept-encoding': 'identity' to the fetch request, but it is a forbidden header and modifying it via code has no effect.
The specs have changed quite recently here. Here is the link to the PR.
TLDR; They now ask the UA that the Acccept-Encoding/Identity header be added to all the Range-requests.
[§5.15]
If httpRequest’s header list contains Range, then append Accept-Encoding/identity to httpRequest’s header list.
Firefox has not yet followed up here, but a bug report has been filled.
For the time being, the Range requests in Firefox indeed are made with the Gzipped data, and thus, you must not break the bytes integrity (for instance the range 0-100 is decode-able in Firefox).
I'm downloading a ~50MB file in 5 MB chunks using XMLHttpRequest and the Range header. Things work great, except for detecting when I've downloaded the last chunk.
Here's a screenshot of the request and response for the first chunk. Notice the Content-Length is 1024 * 1024 * 5 (5 MB). Also notice that the server responds correctly with the first 5 MB, and in the Content-Range header, properly specifies the size of the entire file (after the /):
When I copy the response body into a text editor (Sublime), I only get 5,242,736 characters instead of the expected 5,242,880 as indicated by Content-Length:
Why are 144 characters missing? This is true of every chunk that gets downloaded, though the exact difference varies a little bit.
However, what's especially strange is the last chunk. The server responds with the last ~2.9 MB of the file (instead of a whole 5 MB) and apparently properly indicates this in the response:
Notice that I am requesting the next 5 MB (even though it goes beyond the total file size). No biggie, the server responds with the last part of the file and the headers indicate the actual byte range returned.
But does it really?
When I call xhr.getResponseHeader("Content-Length") with Javascript, I see a different story in Chrome:
The XMLHttpRequest object is telling me that another 5 MB was downloaded, beyond the end of the file. Is there something I don't understand about the xhr object?
What's even weirder is that it works in Firefox 30 as expected:
So between the xhr.responseText.length not matching the Content-Length and these headers not agreeing between the xhr object and the Network tools, I don't know what to do to fix this.
What's causing these discrepancies?
Update: I have confirmed that the server itself is properly sending the request, despite the overshot Range header in the request for the last chunk. This is the output from the raw HTTP request, thanks to good 'ol telnet:
HTTP/1.1 206 Partial Content
Server: nginx/1.4.5
Date: Mon, 14 Jul 2014 21:50:06 GMT
Content-Type: application/octet-stream
Content-Length: 2987360
Last-Modified: Sun, 13 Jul 2014 22:05:10 GMT
Connection: keep-alive
ETag: "53c30296-2fd9560"
Content-Range: bytes 47185920-50173279/50173280
So it looks like Chrome is malfunctioning. Should this be filed as a bug? Where?
The main issue is that you are reading binary data as text. Note that the server responds with Content-Type: application/octet-stream which doesn't specify the encoding explicitly - in that case the browser will typically assume that the data is encoded in UTF-8. While the length will mostly be unchanged (bytes with values 0 to 127 are interpreted as a single character in UTF-8 and bytes with higher values will usually be replaced by the replacement character �), your binary file will certainly contain a few valid multi-byte UTF-8 sequences - and these will be combined into one character. That explains why responseText.length doesn't match the number of bytes received from the server.
Now you could of course force some specific encoding using request.overrideMimeType() method, ISO 8859-1 would make sense in particular because the first 256 Unicode code points are identical with ISO 8859-1:
request.overrideMimeType("application/octet-stream; charset=iso-8859-1");
That should make sure that one byte will always be interpreted as one character. Still, a better approach would be storing the server response in an ArrayBuffer which is explicitly meant to deal with binary data.
var request = new XMLHttpRequest();
request.open(...);
request.responseType = "arraybuffer";
request.send();
...
var array = new Uint8Array(request.response);
alert("First byte has value " + array[0]);
alert("Array length is " + array.length);
According to MDN,
responseType = "arraybuffer" is supported starting with Chrome 10, Firefox 6 and Internet Explorer 10. See also: Typed arrays.
Side-note: Firefox also supports responseType = "moz-chunked-text" and responseType = "moz-chunked-arraybuffer" starting with Firefox 9 which allow receiving data in chunks without resorting to ranged requests. It seems that Chrome doesn't plan to implement it, instead they are working on implementing the Streams API.
Edit: I was unable to reproduce your issue with Chrome lying to you about the response headers, at least not without your code. However, the code responsible should be this function in partial_data.cc:
// We are making multiple requests to complete the range requested by the user.
// Just assume that everything is fine and say that we are returning what was
// requested.
void PartialData::FixResponseHeaders(HttpResponseHeaders* headers,
bool success) {
if (truncated_)
return;
if (byte_range_.IsValid() && success) {
headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
return;
}
This code will remove the Content-Length and Content-Range headers returned by the server and replace them by ones generated from your request parameters. Given that I cannot reproduce the issue myself, the following is only guesses:
This code path seems to be used only for requests that can be satisfied from cache, so I guess that things will work correctly if you clear your cache.
resource_size_ variable must have a wrong value in your case, larger than the actual size of the requested file. This variable is determined from the Content-Range header in the first chunk requested, maybe you have a server response cached there which indicates a larger file.
I am trying to get a response xml which has special characters in it.
This is failing in IE but in Mozilla it is working fine.
Pls let me know how to fix this.
Here's the code:
request.setCharacterEncoding("UTF-8");
response.setContentType("text/xml; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.getWriter().write("<xml><valid><![CDATA[2189971_Bright Starts bath time foam ©®!#& toys each]]></valid><productid>123</productid></xml>");
Try adding the encoding in the XML itself:
response.getWriter().write("<?xml version=\"1.0\" encoding=\"UTF-8\"?><root><valid><![CDATA[2189971_Bright Starts bath time foam ©®!#& toys each]]></valid><productid>123</productid></root>");
Most likely your XML is invalid - you are specifying UTF8 encoding in the XML but writing code probably does not output UTF8. Check out what browser receives with some HTTP watcher (likle Fiddler) to make sure response is properly UTF8 encoded (the characters you are having problem with must be encode as their are above ASCII range).
Not sure what language/framework you are using, but setting encoding on request and writing to response looks suspicious.
When using xmlhttpreqeust the post/get request fails when the data exceeded 7k. (HTTP error 400)
When posting the same data using regular form submission ( etc . . .) it works well.
Is there a limit to data size when using xmlhttprequest? or extra setting is needed?
Dev Inv: NetBeans 6.9.1.
Server: Tomcat 6.
Browser: IE8.
When doing a GET, the data is limited by the length of the URL that the browser accept. Some versions of IE had a limit around 2 kB, so you should make sure that the data is well below that. A GET is simply not suited for sending a lot of data.
When doing a POST, the limit is much higher. The webserver has a default limit for the size of the request, which is typically something like 4 MB.
The same limits apply to a request using XMLHTTPRequest and posting a form. It's the method (POST/GET) that makes a difference for the limit.