html5 multiple xmlhttprequest more response - javascript

Hi guys!
I've got a little problem with my HTML5 XMLHttprequest uploader.
I read files with Filereader class from multiple file input, and after that upload one at the time as binary string. On the server I catch the bits on the input stream, put it in tmp file, etc. This part is good. The program terminated normally, send the response, and I see that in the header (eg with FireBug).
But with the JS, I catch only the last in the 'onreadystatechange'.
I don't see all response. Why? If somebody can solve this problem, it will be nice :)
You will see same jQuery and Template, don't worry :D
This is the JS:
function handleFileSelect(evt)
{
var files = evt.target.files; // FileList object
var todo = {
progress:function(p){
$("div#up_curr_state").width(p+"%");
},
success:function(r,i){
$("#img"+i).attr("src",r);
$("div#upload_state").remove();
},
error:function(e){
alert("error:\n"+e);
}
};
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
var row = $('ul#image_list li').length;
row = row+i;
// Closure to capture the file information.
reader.onload = (function(theFile,s) {
return function(e) {
// Render thumbnail.
$("span#prod_img_nopic").hide();
$("div#prod_imgs").show();
var li = document.createElement('li');
li.className = "order_"+s+" active";
li.innerHTML = ['<img class="thumb" id="img'+s+'" src="', e.target.result,
'" title="', escape(theFile.name), '"/><div id="upload_state"><div id="up_curr_state"></div>Status</div>'].join('');
document.getElementById('image_list').insertBefore(li, null);
};
})(f,row);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
//upload the data
//#param object fileInputId input file id
//#param int fileIndex index of fileInputId
//#param string URL url for xhr event
//#param object todo functions of progress, success xhr, error xhr
//#param string method method of xhr event-def: 'POST'
var url = '{/literal}{$Conf.req_admin}{$SERVER_NAME}/{$ROOT_FILE}?mode={$_GET.mode}&action={$_GET.action}&addnew=product&imageupload={literal}'+f.type;
upload(f, row, url, todo);
}
the upload function:
function upload(file, fileIndex, Url, todo, method)
{
if (!method) {
var method = 'POST';
}
// take the file from the input
var reader = new FileReader();
reader.readAsBinaryString(file); // alternatively you can use readAsDataURL
reader.onloadend = function(evt)
{
// create XHR instance
xhr = new XMLHttpRequest();
// send the file through POST
xhr.open(method, Url, true);
// make sure we have the sendAsBinary method on all browsers
XMLHttpRequest.prototype.mySendAsBinary = function(text){
var data = new ArrayBuffer(text.length);
var ui8a = new Uint8Array(data, 0);
for (var i = 0; i < text.length; i++) ui8a[i] = (text.charCodeAt(i) & 0xff);
var bb = new (window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder)();
bb.append(data);
var blob = bb.getBlob();
this.send(blob);
}
// let's track upload progress
var eventSource = xhr.upload || xhr;
eventSource.addEventListener("progress", function(e) {
// get percentage of how much of the current file has been sent
var position = e.position || e.loaded;
var total = e.totalSize || e.total;
var percentage = Math.round((position/total)*100);
// here you should write your own code how you wish to proces this
todo.progress(percentage);
});
// state change observer - we need to know when and if the file was successfully uploaded
xhr.onreadystatechange = function()
{
if(xhr.status == 200 && xhr.readyState == 4)
{
// process success
resp=xhr.responseText;
todo.success(resp,fileIndex);
}else{
// process error
todo.error(resp);
}
};
// start sending
xhr.mySendAsBinary(evt.target.result);
};
}
}
}
and the starter event
document.getElementById('files').addEventListener('change', handleFileSelect, false);

It's a quite small mistake: You forgot to add a var statement:
// create XHR instance
var xhr = new XMLHttpRequest();
// ^^^ add this
With a readystatechange handler function like yours
function() {
if (xhr.status == 200 && xhr.readyState == 4) {
resp=xhr.responseText; // also a missing variable declaration, btw
todo.success(resp,fileIndex);
} else {
todo.error(resp);
}
}
only the latest xhr instance had been checked for their status and readyState when any request fired an event. Therefore, only when the last xhr triggers the event itself the success function would be executed.
Solution: Fix all your variable declarations, I guess this is not the only one (although affecting the behaviour heavily). You also might use this instead of xhr as a reference to the current XMLHttpRequest instance in the event handler.

Related

How do I Convert URL to Blob with javascript or jquery [duplicate]

I'd like to build a simple HTML page that includes JavaScript to perform a form POST with image data that is embedded in the HTML vs a file off disk.
I've looked at this post which would work with regular form data but I'm stumped on the image data.
JavaScript post request like a form submit
** UPDATE ** Feb. 2014 **
New and improved version available as a jQuery plugin:
https://github.com/CoeJoder/jquery.image.blob
Usage:
$('img').imageBlob().ajax('/upload', {
complete: function(jqXHR, textStatus) { console.log(textStatus); }
});
Requirements
the canvas element (HTML 5)
FormData
XMLHttpRequest.send(:FormData)
Blob constructor
Uint8Array
atob(), escape()
Thus the browser requirements are:
Chrome: 20+
Firefox: 13+
Internet Explorer: 10+
Opera: 12.5+
Safari: 6+
Note: The images must be of the same-origin as your JavaScript, or else the browser security policy will prevent calls to canvas.toDataURL() (for more details, see this SO question: Why does canvas.toDataURL() throw a security exception?). A proxy server can be used to circumvent this limitation via response header injection, as described in the answers to that post.
Here is a jsfiddle of the below code. It should throw an error message, because it's not submitting to a real URL ('/some/url'). Use firebug or a similar tool to inspect the request data and verify that the image is serialized as form data (click "Run" after the page loads):
Example Markup
<img id="someImage" src="../img/logo.png"/>
The JavaScript
(function() {
// access the raw image data
var img = document.getElementById('someImage');
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
var dataUrl = canvas.toDataURL('image/png');
var blob = dataUriToBlob(dataUrl);
// submit as a multipart form, along with any other data
var form = new FormData();
var xhr = new XMLHttpRequest();
xhr.open('POST', '/some/url', true); // plug-in desired URL
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
alert('Success: ' + xhr.responseText);
} else {
alert('Error submitting image: ' + xhr.status);
}
}
};
form.append('param1', 'value1');
form.append('param2', 'value2');
form.append('theFile', blob);
xhr.send(form);
function dataUriToBlob(dataURI) {
// serialize the base64/URLEncoded data
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(dataURI.split(',')[1]);
}
else {
byteString = unescape(dataURI.split(',')[1]);
}
// parse the mime type
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
// construct a Blob of the image data
var array = [];
for(var i = 0; i < byteString.length; i++) {
array.push(byteString.charCodeAt(i));
}
return new Blob(
[new Uint8Array(array)],
{type: mimeString}
);
}
})();
References
SO: 'Convert DataURI to File and append to FormData
Assuming that you are talking about embedded image data like http://en.wikipedia.org/wiki/Data_URI_scheme#HTML
****If my assumption is incorrect, please ignore this answer.**
You can send it as JSON using XMLHttpRequest.
Here is sample code: (you may want to remove the header part ('data:image/png;base64,') before sending)
Image
<img id="myimg" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot">
Button
<input id="subbtn" type="button" value="sub" onclick="sendImg()"></input>
Script
function sendImg() {
var dt = document.getElementById("myimg").src;
var xhr = new XMLHttpRequest();
xhr.open("POST", '/Home/Index', true); //put your URL instead of '/Home/Index'
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) { //4 means request finished and response is ready
alert(xhr.responseText);
}
};
var contentType = "application/json";
xhr.setRequestHeader("Content-Type", contentType);
for (var header in this.headers) {
xhr.setRequestHeader(header, headers[header]);
}
// here's our data variable that we talked about earlier
var data = JSON.stringify({ src: dt });
// finally send the request as binary data
xhr.send(data);
}
EDIT
As #JoeCoder suggests, instead of json, you can also use a FormData object and send in Binary format. Check his answer for more details.

How to upload a file with ajax in small chunks and check for fails, re-upload the parts that failed.

I have a file uploaded by a user, and I'd like to achieve the following.
Divide the file into smaller chunks about a megabyte.
Upload each chunk, and wait for it to finish before starting to upload the next chunk.
For every chunk get success or failure report.
Re-upload the failed chunks.
Get progress in percentages.
Here's some rough JavaScript. I'm literally lost. Got some code online and tried modifying it.
$.chunky = function(file, name){
var loaded = 0;
var step = 1048576//1024*1024;
var total = file.size;
var start = 0;
var reader = new FileReader();
reader.onload = function(e){
var d = {file:reader.result}
$.ajax({
url:"../record/c/index.php",
type:"POST",
data:d}).done(function(r){
$('.record_reply_g').html(r);
loaded += step;
$('.upload_rpogress').html((loaded/total) * 100);
if(loaded <= total){
blob = file.slice(loaded,loaded+step);
reader.readAsBinaryString(blob);
} else {
loaded = total;
}
})
};
var blob = file.slice(start,step);
reader.readAsBinaryString(blob);
}
How can I achieve the above. Please do explain what's happening if there's a viable solution.
You are not doing anything for failure of any chunk upload.
$.chunky = function(file, name){
var loaded = 0;
var step = 1048576//1024*1024; size of one chunk
var total = file.size; // total size of file
var start = 0; // starting position
var reader = new FileReader();
var blob = file.slice(start,step); //a single chunk in starting of step size
reader.readAsBinaryString(blob); // reading that chunk. when it read it, onload will be invoked
reader.onload = function(e){
var d = {file:reader.result}
$.ajax({
url:"../record/c/index.php",
type:"POST",
data:d // d is the chunk got by readAsBinaryString(...)
}).done(function(r){ // if 'd' is uploaded successfully then ->
$('.record_reply_g').html(r); //updating status in html view
loaded += step; //increasing loaded which is being used as start position for next chunk
$('.upload_rpogress').html((loaded/total) * 100);
if(loaded <= total){ // if file is not completely uploaded
blob = file.slice(loaded,loaded+step); // getting next chunk
reader.readAsBinaryString(blob); //reading it through file reader which will call onload again. So it will happen recursively until file is completely uploaded.
} else { // if file is uploaded completely
loaded = total; // just changed loaded which could be used to show status.
}
})
};
}
EDIT
To upload failed chunk again you can do following :
var totalFailures = 0;
reader.onload = function(e) {
....
}).done(function(r){
totalFailures = 0;
....
}).fail(function(r){ // if upload failed
if((totalFailure++) < 3) { // atleast try 3 times to upload file even on failure
reader.readAsBinaryString(blob);
} else { // if file upload is failed 4th time
// show message to user that file uploading process is failed
}
});
I've modified afzalex's answer to use readAsArrayBuffer(), and upload the chunk as a file.
var loaded = 0;
var reader = new FileReader();
var blob = file.slice(loaded, max_chunk_size);
reader.readAsArrayBuffer(blob);
reader.onload = function(e) {
var fd = new FormData();
fd.append('filedata', new File([reader.result], 'filechunk'));
fd.append('loaded', loaded);
$.ajax(url, {
type: "POST",
contentType: false,
data: fd,
processData: false
}).done(function(r) {
loaded += max_chunk_size;
if (loaded < file.size) {
blob = file.slice(loaded, loaded + max_chunk_size);
reader.readAsArrayBuffer(blob);
}
});
};

How to create a Blob from a local file in phonegap on android

I am trying to upload a file to amazon S3 from a phonegap app on android. I already have the code working for iOS. But I've got a trouble making it work on android. The problem is that I do not know how to create the Blob object properly. On iOS I just do this:
blob = new Blob([evt.target.result], {type: "image/png"});
It is uploaded just fine. On android one can not use the Blob constructor (see here), but I could not manage to get the file data correctly into the Blob object using WebKitBlobBuilder.
Here is how I retrieve the file data, there are two approaches and both pass without errors, but the resulting file on the S3 is empty:
window.resolveLocalFileSystemURI(url, function(e){
e.file(function(f){
var reader = new FileReader();
reader.onloadend = function(evt) {
// This way also did not work:
// var builder = new WebKitBlobBuilder();
// builder.append(evt.target.result);
// blob = builder.getBlob("image/png");
var blob = null;
var builder = new WebKitBlobBuilder();
for(var i = 0; i < evt.target.result.length; i++){
builder.append(evt.target.result[i]);
}
blob = builder.getBlob("image/png");
uploadToS3(filename, s3url, blob);
};
reader.readAsArrayBuffer(f);
});
}, function(e){
console.log("error getting file");
error();
});
also, here is the uploadToS3 function:
var uploadToS3 = function(filename, s3url, fileData) {
var xhr = createCORSRequest('PUT', s3url);
if(!xhr) {
console.log('CORS not supported');
error();
return;
}
xhr.onload = function () {
if(xhr.status == 200) {
console.log('100% - Upload completed.');
callback(filename); //callback defined in outer context
}
else {
console.log('0% - Upload error: ' + xhr.status);
console.log(xhr.responseText);
error();
}
};
xhr.onerror = function () {
console.log(0, 'XHR error.');
error();
};
xhr.upload.onprogress = function (e) {
if(e.lengthComputable) {
var percentLoaded = Math.round((e.loaded / e.total) * 100);
var label = (percentLoaded == 100) ? 'Finalizing.' : 'Uploading.';
console.log(percentLoaded + "% - " + label);
}
};
xhr.setRequestHeader('Content-Type', "image/png");
//xhr.setRequestHeader('x-amz-acl', 'public-read');
xhr.send(fileData);
};
EDIT:
I checked the filesize by logging evt.target.result.byteLength and it was ok, so evt.target.result contains image data. Still there is a problem with the upload - I checked the s3 storage and file size is 0, so I am not constructing the Blob correctly.
So this is an android bug after all, which was not fixed since at least Nov 2012.
Here is an article I found which is directly related to my problem: https://ghinda.net/article/jpeg-blob-ajax-android/
It also provides a workaround for the bug, which is to send ArrayBuffer directly, without creating a Blob. In my case I had to send evt.target.result directly.

Show video blob after reading it through AJAX

While I was trying to create a workaround for Chrome unsupporting blobs in IndexedDB I discovered that I could read an image through AJAX as an arraybuffer, store it in IndexedDB, extract it, convert it to a blob and then show it in an element using the following code:
var xhr = new XMLHttpRequest(),newphoto;
xhr.open("GET", "photo1.jpg", true);
xhr.responseType = "arraybuffer";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
newphoto = xhr.response;
/* store "newphoto" in IndexedDB */
...
}
}
document.getElementById("show_image").onclick=function() {
var store = db.transaction("files", "readonly").objectStore("files").get("image1");
store.onsuccess = function() {
var URL = window.URL || window.webkitURL;
var oMyBlob = new Blob([store.result.image], { "type" : "image\/jpg" });
var docURL = URL.createObjectURL(oMyBlob);
var elImage = document.getElementById("photo");
elImage.setAttribute("src", docURL);
URL.revokeObjectURL(docURL);
}
}
This code works fine. But if I try the same process, but this time loading a video (.mp4) I can't show it:
...
var oMyBlob = new Blob([store.result.image], { "type" : "video\/mp4" });
var docURL = URL.createObjectURL(oMyBlob);
var elVideo = document.getElementById("showvideo");
elVideo.setAttribute("src", docURL);
...
<video id="showvideo" controls ></video>
...
Even if I use xhr.responseType = "blob" and not storing the blob in IndexedDB but trying to show it immediately after loading it, it still does not works!
xhr.responseType = "blob";
xhr.addEventListener("load", function () {
if (xhr.status === 200) {
newvideo = xhr.response;
var docURL = URL.createObjectURL(newvideo);
var elVideo = document.getElementById("showvideo");
elVideo.setAttribute("src", docURL);
URL.revokeObjectURL(docURL);
}
}
The next step was trying to do the same thing for PDF files, but I'm stuck with video files!
This is a filler answer (resolved via the OP found in his comments) to prevent the question from continuing to show up under "unanswered" questions.
From the author:
OK, I solved the problem adding an event that waits for the
video/image to load before executing the revokeObjectURL method:
var elImage = document.getElementById("photo");
elImage.addEventListener("load", function (evt) { URL.revokeObjectURL(docURL); }
elImage.setAttribute("src", docURL);
I suppose the revokeObjectURL method was executing before the video
was totally loaded.

How can I upload an embedded image with JavaScript?

I'd like to build a simple HTML page that includes JavaScript to perform a form POST with image data that is embedded in the HTML vs a file off disk.
I've looked at this post which would work with regular form data but I'm stumped on the image data.
JavaScript post request like a form submit
** UPDATE ** Feb. 2014 **
New and improved version available as a jQuery plugin:
https://github.com/CoeJoder/jquery.image.blob
Usage:
$('img').imageBlob().ajax('/upload', {
complete: function(jqXHR, textStatus) { console.log(textStatus); }
});
Requirements
the canvas element (HTML 5)
FormData
XMLHttpRequest.send(:FormData)
Blob constructor
Uint8Array
atob(), escape()
Thus the browser requirements are:
Chrome: 20+
Firefox: 13+
Internet Explorer: 10+
Opera: 12.5+
Safari: 6+
Note: The images must be of the same-origin as your JavaScript, or else the browser security policy will prevent calls to canvas.toDataURL() (for more details, see this SO question: Why does canvas.toDataURL() throw a security exception?). A proxy server can be used to circumvent this limitation via response header injection, as described in the answers to that post.
Here is a jsfiddle of the below code. It should throw an error message, because it's not submitting to a real URL ('/some/url'). Use firebug or a similar tool to inspect the request data and verify that the image is serialized as form data (click "Run" after the page loads):
Example Markup
<img id="someImage" src="../img/logo.png"/>
The JavaScript
(function() {
// access the raw image data
var img = document.getElementById('someImage');
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
var dataUrl = canvas.toDataURL('image/png');
var blob = dataUriToBlob(dataUrl);
// submit as a multipart form, along with any other data
var form = new FormData();
var xhr = new XMLHttpRequest();
xhr.open('POST', '/some/url', true); // plug-in desired URL
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
alert('Success: ' + xhr.responseText);
} else {
alert('Error submitting image: ' + xhr.status);
}
}
};
form.append('param1', 'value1');
form.append('param2', 'value2');
form.append('theFile', blob);
xhr.send(form);
function dataUriToBlob(dataURI) {
// serialize the base64/URLEncoded data
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0) {
byteString = atob(dataURI.split(',')[1]);
}
else {
byteString = unescape(dataURI.split(',')[1]);
}
// parse the mime type
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
// construct a Blob of the image data
var array = [];
for(var i = 0; i < byteString.length; i++) {
array.push(byteString.charCodeAt(i));
}
return new Blob(
[new Uint8Array(array)],
{type: mimeString}
);
}
})();
References
SO: 'Convert DataURI to File and append to FormData
Assuming that you are talking about embedded image data like http://en.wikipedia.org/wiki/Data_URI_scheme#HTML
****If my assumption is incorrect, please ignore this answer.**
You can send it as JSON using XMLHttpRequest.
Here is sample code: (you may want to remove the header part ('data:image/png;base64,') before sending)
Image
<img id="myimg" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot">
Button
<input id="subbtn" type="button" value="sub" onclick="sendImg()"></input>
Script
function sendImg() {
var dt = document.getElementById("myimg").src;
var xhr = new XMLHttpRequest();
xhr.open("POST", '/Home/Index', true); //put your URL instead of '/Home/Index'
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) { //4 means request finished and response is ready
alert(xhr.responseText);
}
};
var contentType = "application/json";
xhr.setRequestHeader("Content-Type", contentType);
for (var header in this.headers) {
xhr.setRequestHeader(header, headers[header]);
}
// here's our data variable that we talked about earlier
var data = JSON.stringify({ src: dt });
// finally send the request as binary data
xhr.send(data);
}
EDIT
As #JoeCoder suggests, instead of json, you can also use a FormData object and send in Binary format. Check his answer for more details.

Categories