Send a File as Multipart Through Xmlhttprequest

Send a file as multipart through XMLHttpRequest

That's only possible with the XHR FormData API (previously known being part of as "XHR2" or "XHR Level 2", currently known as "XHR Advanced Features").

Given this HTML,

<input type="file" id="myFileField" name="myFile" />

you can upload it as below:

var formData = new FormData();
formData.append("myFile", document.getElementById("myFileField").files[0]);

var xhr = new XMLHttpRequest();
xhr.open("POST", "myServletUrl");
xhr.send(formData);

XHR will take care about proper headers and request body encoding and the file will in this example be available on the server side as form-data part with the name myFile.

You need to keep in mind that FormData API is not supported in older browsers. At caniuse.com you can see that it's currently implemented in Chrome 7+, Firefox 3.5+, Safari 5+, Internet Explorer 10+ and Opera 12+.

In case you're using jQuery, then you might be tempted to use its $.val() function as below:

formData.append("myFile", $("#myFileField").val());

But this is incorrect as it doesn't return the whole File object, but merely the file name as String which is utterly useless as it doesn't contain the file contents.

If you don't want to use document.getElementById() for some reason, then use one of the following instead:

formData.append("myFile", $("#myFileField").prop("files")[0]);
formData.append("myFile", $("#myFileField")[0].files[0]);

An alternative is to use the jQuery Form plugin. Your entire form, when written and functioning properly without any line of JavaScript code, will then instantly be ajaxified with just the following line:

$("#formId").ajaxForm(function(response) {
// Handle Ajax response here.
});

It also supports file uploads as well by a hidden iframe trick. See also this jQuery Form documentation for an in-depth explanation. You may only need to change the servlet code to be able to intercept on both normal (synchronous) and Ajax (asynchronous) requests. See also this answer for a concrete example: Simple calculator with JSP/Servlet and Ajax

Either way, the uploaded file should then be available in the doPost() method of a @MultipartConfig servlet as follows:

Part myFile = request.getPart("myFile");

Or if you're still on Servlet 2.5 or older, use Apache Commons FileUpload the usual way. See also this answer for a concrete example: How can I upload files to a server using JSP/Servlet?

How do I upload a file using FormData and XmlHTTPRequest?

I found two issues in your code:

  1. In JavaScript code, you explicitly defined the "Content-Type" as "multipart/form-data". JS automatically gets the content type when you make the XHR Request.
  2. In the 4th line of your PHP code, the key of the $_FILE is "file". You need to change it to "myfile", in order to make it work.

You can check my edited code below:

JS Code:

function uploadFile() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
console.log('success');
} else {
console.log('error');
}
}
};
xhr.open('POST','php/parsefile.php',true);
var formData = new FormData();
formData.append("myfile", document.querySelector('#fileInput').files[0]);
xhr.send(formData);
}

PHP Code:

<?php
echo "Name: ". $_FILES['myfile']['name'];
echo "Error: ".$_FILES['myfile']['error'];

XMLHttpRequest POST multipart/form-data

You can construct the 'multipart/form-data' request yourself (read more about it at http://www.faqs.org/rfcs/rfc2388.html) and then use the send method (ie. xhr.send(your-multipart-form-data)). Similarly, but easier, in Firefox 4+ (also in Chrome 5+ and Safari 5+) you can use the FormData interface that helps to construct such requests. The send method is good for text content but if you want to send binary data such as images, you can do it with the help of the sendAsBinary method that has been around starting with Firefox 3.0. For details on how to send files via XMLHttpRequest, please refer to http://blog.igstan.ro/2009/01/pure-javascript-file-upload.html.

Javascript XHR send multipart/form-data

Your code failed because you've used "Enter" instead of an escaped line break character (\n).

JavaScript doesn't support "first line[Enter]second line". If you need a string with a line break, use "first line\nsecond line".

Once you've fixed this problem, your code should work as intended (with one caveat, see final note):

var xhr = new XMLHttpRequest();
xhr.onload = function() {
alert(xhr.responseText);
};
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type","multipart/form-data; boundary=---------------------------275932272513031");
xhr.send('-----------------------------275932272513031\n' +
'Content-Disposition: form-data; name="name"\n\n' +
'test\n\n' +
'----------------------------275932272513031--');

NOTE: Your code will only work for payloads that consists of UTF-8 characters, not binary data. If you want to learn more about submitting forms with binary data via XMLHttpRequest, see this answer and its linked references.

how to use multipart to upload file using XMLHttpRequest?

Until the upcoming XMLHttpRequest version 2, you cannot upload a file using Ajax.

Most of the current Ajaxbased file uploaders use an <iframe> hack. It uses JS code to create an invisible <iframe> where the form is been copied in and is been submitted synchronously. The parent page will just stay unchanged and it looks like as if it is been done asynchronously.

To get best crossbrowser compatibility and to minimize headaches with regard to writing crossbrowser compatible code, I'd strongly recommend to grab an existing JS library which excels in handling ajaxical stuff and traversing/manipulating HTML DOM tree, such as jQuery. It comes with plethora of form upload plugins, the simplest one being the jQuery-form plugin. It supports file uploads as well with help of the hidden <iframe> hack. It's then basically as easy as

<script src="jquery.js"></script>
<script src="jquery-form.js"></script>
<script>
$(document).ready(function() {
$('#formid').ajaxForm(function(data) {
// Do something with response.
$('#result').text(data.result);
});
});
</script>
...
<form id="formid" action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" />
</form>
<div id="result"></div>

In the server side, just have a servlet which processes the request the usual way, either with the Servlet 3.0 provided HttpServletRequest#getParts() or with the good old Apache Commons FileUpload (examples here).

Either way, you can just return the response as JSON the usual way

Map<String, Object> data = new HashMap<String, Object>();
data.put("result", "Upload successful!");
// ...
response.setContentType("application/json");
resposne.setCharacterEncoding("UTF-8");
resposne.getWriter().write(new Gson().toJson(data));

For more Ajax-Servlet-JSON examples, check this answer.

Multipart form data using XHR - Invalid Request

You've explicitly set the Content-Type but it is missing the MIME boundary parameter. Omit the header entirely and allow XHR to infer the Content-Type from the FromData object.

Upload file with Ajax XMLHttpRequest

  1. There is no such thing as xhr.file = file;; the file object is not supposed to be attached this way.
  2. xhr.send(file) doesn't send the file. You have to use the FormData object to wrap the file into a multipart/form-data post data object:

    var formData = new FormData();
    formData.append("thefile", file);
    xhr.send(formData);

After that, the file can be access in $_FILES['thefile'] (if you are using PHP).

Remember, MDC and Mozilla Hack demos are your best friends.

EDIT: The (2) above was incorrect. It does send the file, but it would send it as raw post data. That means you would have to parse it yourself on the server (and it's often not possible, depend on server configuration). Read how to get raw post data in PHP here.



Related Topics



Leave a reply



Submit