Download a file and save to blob (without saving to file) - javascript

I'm using xmlhttprequest to download files(binary data, e.g., PDF) and save to blob (in memory instead of disk). The number of bytes downloaded is not correct. Can you give me some suggestions?
var url = ... // target file's url
var file_content = null;
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET",url,false);
xmlhttp.send();
if(xmlhttp.status == 200){
file_content =xmlhttp.response; // file_content's length is not correct.
}
var blob = new Blob([file_content],{type: 'text/plain'} );

Related

javascript createObjectURL corrupt file

im using the following javascript to create an object url, the only problem is when loading the url blob:http:///mysite.com/randomhash the file is corrupt.
var audioFile = null;
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
audioFile = new Blob([xhttp.response], {type: 'audio/mp3'});
}
};
xhttp.open("GET", "myMp3File.mp3", false);
xhttp.send();
var file = new File([audioFile], "myMp3File.mp3", {type: "audio/mp3", lastModified: Date.now()});
any ideas as to why this would create a blob url with a corrupt mp3 ?
Multiple problems here.
First, you are dealing with asynchronous code. You will need to use a callback in order to use the response of your XHR request.
Then, even if you did so, there are good chances that it will still not work.
This is because the response you get is plain text UTF16 and that some bytes will get mangled by encoding.
The real solution in your case is to directly request the response as a Blob.
You can do so with the XMLHttpRequest.responseType parameter.
var xhttp = new XMLHttpRequest();
xhttp.responseType = 'blob';
xhttp.onload = function(e) {
var blob = xhttp.response;
callback(blob);
};
xhttp.open...
And now in your callback you will be able to create a blobURI directly from this blob.
function callback(blob) {
var url = URL.createObjectURL(blob);
...
Also, if all you want is to display this file, there is no point in creating a File from it, the only web API it would make a difference if it was a File and not just a Blob is Formdata.append method, and even there, it's not that useful.

Downloading a base64 byte array into a file using ajax call

I have the below problem.
I need to send multiple files attached as part of JSON request and save it into database.
followed the below steps:
converted the uploaded file into base64 String and included in JSON with other values.
In server side, using java, converted the base64 string to byte[] and stored in database.
Issue with downloading the file retrieved from database.
I have written a controller method to return the byte[] from database and using XMLHttpRequest, i am trying to save the file. But getting file corrupted error while opening the saved file.
below is my javascript code:
var request = new XMLHttpRequest();
request.open("POST", 'getAttachment/'+idTemp, true);
request.responseType = "blob";
request.onload = function (e) {
if (this.status === 200) {
// `blob` response
console.log(this.response);
var arr = this.response;
var byteArray = new Uint8Array(arr);
// create `objectURL` of `this.response` : `.pdf` as `Blob`
var file = window.URL.createObjectURL(new Blob([byteArray]));
var a = document.createElement("a");
a.href = file;
a.download = this.response.name || fileNameTemp;
document.body.appendChild(a);
a.click();
}
}
request.send();
Please note that, all types of files has to be stored and downloaded.

InboxSDK - Unable to attachFiles through composeView object

I am trying to attach an attachment through the composeView object using Inboxsdk. I obtain a blob object from a remote url using the following code.
// FUNCTION TO OPEN A NEW COMPOSE BOX ONCE THE ATTACHMENT BLOB IS OBTAINED FROM REMOTE URL.
function openComposeBox(sdk, blob){
handler = sdk.Compose.registerComposeViewHandler(function(composeView){
if(blob != null){
composeView.attachFiles([blob]);
composeView.setSubject("Testing");
}
});
}
// FETCHING ATTACHMENT FILE FROM REMOTE URL
var file_btn_url = "https://api.hummingbill.com/system/invoices/invoice_files/000/033/393/original/abracadabra.txt";
var file_name = file_btn_url.split("/");
file_name = file_name[file_name.length-1];
file_type = /[^.]+$/.exec(file_name);
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET", file_btn_url);
xhr.responseType = "blob";
xhr.onload = function()
{
blob = xhr.response;
// blob.lastModifiedDate = new Date(); // Since it's not necessary to have it assigned, hence commented.
blob.name = file_name;
console.log(blob);
openComposeBox(sdk, blob);
}
xhr.send();
It shows an Attachment Failed error.
Although I have the correct format of blob object as required as per the documentation.
As per the documentation, I have set the filename for the blob, and passed it in an array to attachFiles function. Can you please look into it and let me know what am I missing?
Posting the solution. The code remains same as in the question, with a slight variation, wherein we convert the blob to a file, in order to make it work.
//... Same as the code in the question
// Fetching attachment file from remote url
var file_btn_url = "https://api.hummingbill.com/system/invoices/invoice_files/000/033/393/original/abracadabra.txt";
var file_name = file_btn_url.split("/");
file_name = file_name[file_name.length-1];
file_type = /[^.]+$/.exec(file_name);
var blob = null;
var xhr = new XMLHttpRequest();
xhr.open("GET", file_btn_url);
xhr.responseType = "blob";
xhr.onload = function()
{
// Solution: Convert the obtained blob to a file
// Pass the file to openComposeBox
blob = new Blob([xhr.response], { type: xhr.responseType });
var file = new File([blob], file_name);
blob.name = file_name;
openComposeBox(sdk, file);
}
xhr.send();
Hope this helps. Cheers!

Generate a blob with grunt which will be available to JS in var

I need to embed a Flash .swf on the page and am unable use the normal way of setting the src or data attribute to the swf url - don't ask :s. So, I'm doing an ajax request for the swf, converting to a blob and then generating a blob url which I set as the swf src. Then I realised that as I'm building with Grunt, there may be a way to just write the swf file into the code as a blob in a var, and avoid the ajax request completely. Here's the code with the ajax request:
function createFlashMovie(blobUrl){
var obj = document.createElement("object");
obj.setAttribute("width", "800");
obj.setAttribute("height", "600");
obj.setAttribute("type", "application/x-shockwave-flash");
obj.setAttribute("data", blobUrl);
document.body.appendChild(obj);
}
function onAjaxLoad(oResponse){
blobUrl = window.URL.createObjectURL(oResponse);
createFlashMovie(blobUrl);
};
//do the xhr request for a.swf
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200){
onAjaxLoad(this.response);
}
}
xhr.open('GET', '//theserver.com/a.swf');
xhr.responseType = 'blob';
xhr.send();
...but I'm sure it must be possible to have something like this which is replaced by grunt to have the blob already available when it runs, and go straight to creating the blob url without the xhr request:
var theBlob = new Blob(["GRUNT_WRITES_THIS_IN_FROM_FILE"], {type: "application/x-shockwave-flash"});
Well, grunt is at its core just a Node program, so you can use any node command in your Gruntfile or tasks definitions. And it seems that Node's http.request would be perfect for your needs.
So:
add a custom task in your Gruntfile (http://gruntjs.com/creating-tasks#custom-tasks)
that uses http.request to download your swf (https://docs.nodejitsu.com/articles/HTTP/clients/how-to-create-a-HTTP-request)
update your code to use the local swf
I found a solution. Convert your swf file to be a base64-encoded string. At the moment I'm doing this separately and then pasting it into the source JS, but it can be automated at build time with grunt. Then in the page script create a var to store it as a dataURI:
var swfAsDataUri = "data:application/x-shockwave-flash;base64,BIG_LONG_CHUNK_OF_DATA_THAT_IS_YOUR_ENCODED_SWF_FILE__GRUNT_CAN_WRITE_THIS_IN_AT_BUILD_TIME";
Create a blob from the data url, and then create an object url from the blob:
//function taken from http://stackoverflow.com/questions/27159179/how-to-convert-blob-to-file-in-javascript
dataURLToBlob = function(dataURL) {
var BASE64_MARKER = ';base64,';
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: contentType});
};
var blobUrl = window.URL.createObjectURL( dataURLToBlob(swfAsDataUri) );
You can then use the object url as the src data for the flash movie's object tag when it's embedded:
function createFlashMovie(blobUrl){
var obj = document.createElement("object");
obj.setAttribute("width", "800");
obj.setAttribute("height", "600");
obj.setAttribute("type", "application/x-shockwave-flash");
obj.setAttribute("data", blobUrl); //use the object url here
document.body.appendChild(obj);
}
...and there you have it, no additional http request for the swf file.

How to download and then upload the file?

Tried to use the following code, but it doesn't work properly:
// download the file first
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return '';
// upload the file
req.open("POST", "http://mysite.com/upload", false);
req.setRequestHeader("Content-Length", req.responseText.length);
req.sendAsBinary(req.responseText); // What should I pass here?
if (req.status != 200) return '';
return req.responseText;
sendAsBinary is firefox function.
Upd. Also I've tried to upload that as part of the form:
var response = req.responseText;
var formData = new FormData();
formData.append("file", response);
req.open("POST", "http://mysite.com/upload", false);
req.send(formData);
But still not full data is received by the server.
Finally I've used the approach with temp file:
var downloadCompleted = false;
// download the file first
var persist = Components.classes["#mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
.createInstance(Components.interfaces.nsIWebBrowserPersist);
// get OS temp folder
var file = Components.classes["#mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("TmpD", Components.interfaces.nsIFile);
file.append("temp.ext");
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);
var fURI = Services.io.newURI(url,null,null);
const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
persist.progressListener = {
onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
},
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
if (aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP) {
downloadCompleted = true; // file has been downloaded
}
}
}
persist.saveURI(fURI, null, null, null, "", file);
var thread = Components.classes["#mozilla.org/thread-manager;1"]
.getService(Components.interfaces.nsIThreadManager)
.currentThread;
while (!downloadCompleted) // emulate synchronous request, not recommended approach
thread.processNextEvent(true);
// upload the file
var stream = Components.classes["#mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
stream.init(file, 0x04 | 0x08, 0644, 0x04); // file is an nsIFile instance
// try to determine the MIME type of the file
var mimeType = "text/plain";
try {
var mimeService = Components.classes["#mozilla.org/mime;1"]
.getService(Components.interfaces.nsIMIMEService);
mimeType = mimeService.getTypeFromFile(file); // file is an nsIFile instance
}
catch(e) { /* just use text/plain */ }
var req = Components.classes["#mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
req.open('POST', "http://mysite.com/upload", false);
req.setRequestHeader('Content-Type', mimeType);
req.send(stream);
// delete the file
file.remove(false);
You need to store the responseText in an intermediate variable before reusing the req object.
// download the file first
var req = new XMLHttpRequest();
req.open('GET', url, false);
req.overrideMimeType('text/plain; charset=x-user-defined');
req.send(null);
if (req.status != 200) return '';
var response = req.responseText;
// upload the file
req.open("POST", "http://mysite.com/upload", false);
req.setRequestHeader("Content-Length", response.length);
req.sendAsBinary(response);
if (req.status != 200) return '';
return req.responseText;
Update
Per the MDN page Using XMLHttpRequest, it looks like the above code won't work. Following is the proper way to get the binary response. In the end, you will have an array of unsigned integers which you could send back to the server and convert to binary. I think.
//req.responseType is only defined for FF6+
req.responseType = "arraybuffer";
req.send(null);
//req.response is for FF6+, req.mozResponseArrayBuffer is for FF < 6
var buffer = req.mozResponseArrayBuffer || req.response;
if (buffer) {
var byteArray = new Uint8Array(buffer);
}
Update 2
To submit the byteArray to a server, I would try something like the following untested, almost guaranteed not to work code.
req.open("POST", "http://mysite.com/upload", false);
req.setRequestHeader("Content-Length", byteArray.length);
//if this doesn't work, try byteArray.buffer
//if byteArray.buffer works, try skipping 'var byteArray = new Uint8Array(buffer);' altogether and just sending the buffer directly
req.send(byteArray);
Update 3
Could Using XMLHttpRequest from JavaScript modules / XPCOM components have anything to do with your issue?

Categories

Resources