tl;dr: the two scripts below are not identical although I believe them to be. Why?
I'll give you some background information about this post. I'm trying to build an image uploading form that makes AJAX requests to create a dynamic upload page. Since I learned about HTML5's File API, I'm trying to use it in an AJAX request. I built my own instance by following some nice examples on MDN. My working script uses straight JS for the AJAX request, but I tried building a version that uses jQuery instead. My question is, why won't the jQuery version work properly? As far as I can tell, it's a straight port from JS xhr-style AJAX to jQuery-style AJAX. (The point of this question is to gain an academic understanding of jQuery's AJAX API; since I already have a working script, my practical needs have been fulfilled)
Here is the working Javascript AJAX request:
$("form").submit(function(){
var file = $("input:file").get(0).files[0];
var xhr = new XMLHttpRequest();
var fd = new FormData();
xhr.open("POST", "/upload", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
fd.append('img', file);
xhr.send(fd);
return false;
});
And the non-working jQuery version:
$("form").submit(function(){
var fd = new FormData();
var file = $("input:file").get(0).files[0];
fd.append('img', file);
$.post("/upload", fd, function(data){
alert(data);
});
return false;
});
As posted in the documentation, $.post accepts either a plain object or a string as its data argument. You cannot use FormData.
data
Type: PlainObject or String
A plain object or string that is sent to the server with the request.
jQuery calls $.param on the second argument to $.post (and others) if it is not a string. fd is not a string, but $.param on fd is invalid. If you want to use the raw fd value, you have to set processData to false in the ajax settings: http://api.jquery.com/jQuery.ajax/
$.ajax({
ur: "/upload",
data: fd,
processData: false,
}).done(function (data) {
console.log(data);
});
Related
I'm testing FileSaver saveAs function. Here's my code to fetch report data with post request
$.ajax({
type: "POST",
url: '/rest/report/test',
contentType: 'application/json',
async: false,
data: JSON.stringify({"date": "11.11.2015"}),
success: function (response) {
console.log(response);
saveAs(response,"test.xlsx");
}
});
That fails with error: Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided.
But I can see result console.log(response); - it shows file content. Is it possible to make my code to download file?
saveAs tries to execute createObjectURL on your text, and fails.
The reason is that saveAs does not accept plain text as argument. It only accepts Blob objects.
Text files
If your server returns text, you can create Blob from your text by using new Blob() constructor.
Here is the working example:
document.getElementById('download').onclick = function() {
var text = "Hello world!";
var blob = new Blob([text], {
type: "text/plain; encoding=UTF-8"
});
saveAs(blob, "result.txt");
};
<script src="http://eligrey.com/demos/FileSaver.js/FileSaver.js"></script>
Download
Binary files
As long as you have binary file, you can use native XMLHttpRequest and use responseType = blob.
var xhr = new XMLHttpRequest();
xhr.open('POST', '/rest/report/test/', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var blob = this.response;
saveAs(blob, 'download.xlsx');
}
};
xhr.send();
A bit late for an answer but useful for other programmer
jQuery doesn't allow to download binary files, this is what is needed here. Your call without the datatype probably default to text. Creating a blob from a text string can cause artefact to corrupt the file as the binary data was initially interpret as text.
As proposed by Yeldar answer, you call you own XMLHttpRequest but this don’t allow you to keep your code clean. jQuery allows to create Ajax transports plugins so you will be able to get a binary array contain in a blob and bypassing your costly conversion by directly calling saveAs on your response.
http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/
Github code: https://github.com/henrya/js-jquery/tree/master/BinaryTransport
lets jump right into the code :
var formData = new FormData();
formData.append('name', dogName);
formData.append('weight', dogWeight);
formData.append('activity', dogActivity);
formData.append('age', dogAge);
formData.append('file', document.getElementById("dogImg").files[0]);
console.log(formData);
Here I am appending some strings and one file object to the formData object in order to send all the information asynchronous to the server.
Right after that I have this jquery ajax request :
$.ajax({
type: "POST",
url: "/foodoo/index.php?method=insertNewDog",
data: JSON.stringify(formData),
processData: false, // tell jQuery not to process the data
contentType: "multipart/form-data; charset=utf-8",
success: function(response){
console.log(response);
},
error: function(){
}
});
So here I am trying to POST the info to the server, on the server php file I have a simple print_r of the POST so I see what gets through and what not.
Unfortunately my response in the console.log(data) is empty.
Also if you check the HEADER in the Network tab you get the following empty output:
Success function gets called (just for clarification)
When you're sending an ajax request via jQuery and you want to send FormData you don't need to use JSON.stringify on this FormData. Also when you're sending file the content type must be multipart/form-data including boundry - something like this multipart/form-data; boundary=----WebKitFormBoundary0BPm0koKA
So to send FormData including some file via jQuery ajax you need to:
Set data to the FormData without any modifications.
Set processData to false (Lets you prevent jQuery from automatically transforming the data into a query string).
Set the contentType to false (This is needed because otherwise jQuery will set it incorrectly).
Your request should look like this:
var formData = new FormData();
formData.append('name', dogName);
// ...
formData.append('file', document.getElementById("dogImg").files[0]);
$.ajax({
type: "POST",
url: "/foodoo/index.php?method=insertNewDog",
data: formData,
processData: false,
contentType: false,
success: function(response) {
console.log(response);
},
error: function(errResponse) {
console.log(errResponse);
}
});
if you did exactly as pervious anwswer and still not working
dont worry its working
maybe intelligence and quick wath are telling you its not working
but dont worry, look at network tap
its working
hope this saves your time
//For those who use plain javascript
var form = document.getElementById('registration-form'); //id of form
var formdata = new FormData(form);
var xhr = new XMLHttpRequest();
xhr.open('POST','form.php',true);
// xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
//if you have included the setRequestHeader remove that line as you need the
// multipart/form-data as content type.
xhr.onload = function(){
console.log(xhr.responseText);
}
xhr.send(formdata);
I am trying to export my web page data and download it as excel file. but the download does not start even the response return succeed.
$.ajax({
type: "POST",
url: _url,
contentType: 'multipart/form-data;boundary=SzB12x',
data: json,
});
The responseText something like this:
PK�J;Fxl/theme/theme1.xml�YOo�6����,[r��n;v��i����#-�kJH:�oC{0X7�2��mZ���d��u#�(٦b:M���������{|��^�0t#��*"w$�!0I�[�՚n�i��'����iH� g�,��|�J�!���hRh�h��?r&�L ���߶S��v#���#���"���}��Жt%�hR�t"������+��������u{ނ��0K���oy�9OTWywkAͯ�
���F�� 6*�����[���U���
I think its the file but I cant download it!!
Any help please?
Thanks!
I faced the same issue and successfully solved it. My use-case is this.
Post JSON data to server and receive an excel file.
That excel file is created on the fly and returned as a response to client.
Code:
$("#my-button").on("click", function() {
// Data to post
data = {
ids: [1, 2, 3, 4, 5]
};
// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
var a;
if (xhttp.readyState === 4 && xhttp.status === 200) {
// Trick for making downloadable link
a = document.createElement('a');
a.href = window.URL.createObjectURL(xhttp.response);
// Give filename you wish to download
a.download = "test-file.xls";
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});
The above snippet is just doing following
Posting an array as JSON to the server using XMLHttpRequest
After fetching content as a blob(binary), we are creating a downloadable URL and attaching it to invisible "a" link then clicking it.
Here we need to carefully set few things at the server side. I set few headers in Python Django HttpResponse. You need to set them accordingly if you are use other programming languages.
# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
Since I download xls(excel) here, I adjusted contentType to above one. You need to set it according to your file type.
Try to use a hidden form to submit the request.
When a user submits an HTML form, all the data entered into the form by the user is sent as either a GET or POST request to the URL specified in the “ACTION” attribute of FORM.
<FORM action="http://www.labnol.org/sendmail.php" method="post">
...form contents...
</FORM>
In the above example, an HTTP POST request is issued to the sendmail.php script on form submission. You can add target=”_blank” to the FORM tag to process the request in a new window.
However, if you would like to submit a FORM on the page in the background without directing the browser to another page (document.location.href changes on form submit), you have two options:
Option #1. You can either create an invisible IFRAME inside your HTML page and set that as a target for the Original FORM. This will submit the form but without reloading the parent window.
<FORM action="http://example.com/script.php"
method="POST" target="hidden-form">
...form contents...
</FORM>
<IFRAME style="display:none" name="hidden-form"></IFRAME>
Option #2: There’s another method that allows you create custom payloads before submitting the form. Unlike the IFRAME based form submission, the following code makes a standard form submit request and thus your browser location will change and the current page will get added to the browser history. Credit: Rakesh Pai.
submitFORM('http://example.com/script.php', 'POST',
{'name':'digital+inspiration', 'age':'100', 'sex','M'});
function submitFORM(path, params, method) {
method = method || "post";
var form = document.createElement("form");
form.setAttribute("method", method);
form.setAttribute("action", path);
//Move the submit function to another variable
//so that it doesn't get overwritten.
form._submit_function_ = form.submit;
for(var key in params) {
if(params.hasOwnProperty(key)) {
var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", key);
hiddenField.setAttribute("value", params[key]);
form.appendChild(hiddenField);
}
}
document.body.appendChild(form);
form._submit_function_();
}
In this link you can find the way to create hidden form and submit it.
enjoy!!
The approach here is directly lifted from https://gist.github.com/DavidMah/3533415.
This approach uses <form> and appends the data with a key. This approach works if the server is already expecting the data as an attribute of the request body, as opposed to being the request body itself. If the data to be uploaded is an object, you could iterate over that object's keys. If the data to be uploaded is an array, either modify the server route or [add idea here].
In browser
// Takes a URL, param name, and data string
// Sends to the server... The server can respond with binary data to download
jQuery.download = function(url, key, data) {
// Build a form
var form = $('<form></form>').attr('action', url).attr('method', 'post');
// Add the one key/value
form.append($("<input></input>").attr('type', 'hidden').attr('name', key).attr('value', data));
//send request
form.appendTo('body').submit().remove();
};
On server
# A Tidbit of sinatra code to respond
# Assume 'url' is a set variable
# Assume 'key' is the key of the value used in the javascript
post url do
data = params[:key]
puts request.body.read
headers['Content-Type'] = "application/octet-stream"
body(data)
end
Example
$.download('/path/resource/', 'data', JSON.stringify(data))
If you just want to download a file, you don't need to use ajax to do it. Actually, you cannot download file using ajax.
You can still do it by making a hyperlink Export request to a server page that responses content-type is application/vnd.ms-excel and content-disposition is attachment.
You can achieve this using an iFrame as well. A sample function:
// append the data to URL
var requestData = {
param1 : "value1",
param2 : "value2",
}
// call the function
downloadFile(<your_URL>, requestData);
function downloadFile(requestURL, data) {
// "transData" is just a user defined variable to encapsulate "downloadIFrame". It can be named anything as required.
var downloadIFrame = window.transData.downloadIFrame = window.transData.downloadIFrame || $("#downloadFileiFrame");
downloadIFrame.attr("src", requestURL + $.param(requestData));
}
// define the iFrame in your HTML and hide it.
<iframe id="downloadFileiFrame" style="display:none;"></iframe>"
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: yoururlpath,
success: function (response) {
var file = fileName+".xlsx";
window.location = "someFilePath?file=" + file;
}
});
I am using the plugin jquery.form.js and I want to programmatically submit a form and include a file with it. The code and options I have set up with work with $.ajax but not .ajaxSubmit. According to the jquery.form.js documentation you can pass any standard $.ajax options to .ajaxSubmit but I can't seem to get it to work. I'd like to use .ajaxSubmit if possible so I can utilize some of the other features it offers.
$(document).ready(function() {
$('#file-form').submit(function(event) {
event.preventDefault();
var form = $("<form style='display:none;' method='post' action='video_upload.php' enctype='multipart/form-data'></form>");
var fd = new FormData();
fd.append('uploadedfile', $('#file')[0].files[0]);
var options = {
url: 'video_upload.php',
data: fd,
processData: false,
contentType: false,
type: 'POST',
beforeSend: function(xhr){
alert('start');
},
success: function(data){
alert(data);
}
};
$.ajax(options);
//form.ajaxSubmit(options);
return false;
});
});
Running $.ajax(options) works but form.ajaxSubmit(options) doesn't. What am I missing?
Thanks!
If you check the source code of the method ajaxSubmit - http://malsup.github.io/jquery.form.js you can see that the property data of the options is serialised/deserialised and converted several times. So most likely the content really submitted will be quite different from what happens in .ajax call. Later on the ajaxSubmit collects the files from form and submits them differently.
Basically, for me the specifying data while submitting with ajaxSubmit goes against the concept of this plugin, which is described as "The main methods, ajaxForm and ajaxSubmit, gather information from the form element to determine how to manage the submit process." Idiomatic way of using ajaxSubmit would be applying this method for the form with controls.
My app uses many blob references to local image files created with with createObjectURL.
I need to convert these blob url references into what is essentially a javascript file object in order to upload them.
To retrieve the blobs I have jquery's Ajax function as show below:
var bloburl = 'blob:3c9230a9-55ea-4357-a2d3-db97673a1947';
$.ajax({
url : bloburl, //Blob URL reference
type : 'GET',
processData : false,
contentType : false,
success: function(file) {
//Here I am attempting to build an object that can be read by the FileReader API
var blob = new Blob([file], { type: 'image/jpeg' });
self.cache.filestoupload.push(blob);
}
});
Once the self.cache.filestoupload array has been populated the app begins to attack the first file in the array, slicing the first few bytes, then reading it as a binary string via the fileReader API. This slice of the file is then passed into a webworker and the image exif data is read. Then finally it begins to upload the full file.
var binaryReader = new FileReader();
binaryReader.onload = function (e) {
worker.postMessage({
guid: fileindex,
binary_string: binaryReader.result
});
};
binaryReader.readAsBinaryString(filePart);
Everything works perfectly with files retrieved in the standard manner from an HTML <input> element.
However the very same file when referenced via a blob URL, retrieved via an ajax request and then sliced and passed into the fileReader API fails. No exif data is returned and although the file uploads to the server the very same PHP script which handles the uploads fine also fails.
It appears that I am not retrieving the file correctly. Where am I going wrong?
To summarize, I need to be able to retrieve files referenced with the createObjectURL and pass them into the fileReader in the same format as if they were a standard javascript file object
UPDATE:
Okay I have made it work using a standard xhr request as follows:
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onreadystatechange = function(e) {
if (xhr.readyState == 4) {
var myBlob = this.response;
self.cache.filestoupload.push(myBlob);
}
};
xhr.send();
How can I do the same using jQuery's $.Ajax method?
You cannot currently do this in jQuery 1.x or 2.x. See: https://github.com/jquery/jquery/pull/1525