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:
- 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.
- 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
- There is no such thing as
xhr.file = file;
; the file object is not supposed to be attached this way. xhr.send(file)
doesn't send the file. You have to use theFormData
object to wrap the file into amultipart/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
What Is Right Way to Do API Call in React Js
The Best Way to Synchronize Client-Side JavaScript Clock with Server Date
How to Pass Data from JavaScript to Swift Within a Wkwebview
Error: Require() of Es Modules Is Not Supported When Importing Node-Fetch
How to Implement Inheritance in Js Revealing Prototype Pattern
Finding Variable Type in JavaScript
Headless Browser for Python (JavaScript Support Required!)
Calling Member Function of Number Literal
Angular4 - No Value Accessor for Form Control
Angular 2 Dependency Injection in Es5 and Es6
Combined Comparison/"Spaceship" Operator (<=>) in JavaScript
Test If a Data Exist in Firebase
React Native: Require() with Dynamic String
Passing Array of Objects from Js to Rails
Pdf.Js: Rendering a PDF File Using a Base64 File Source Instead of Url
How to Control the Back Button Event in Jquery Mobile
Passing Functions to Settimeout in a Loop: Always the Last Value