Firebase Storage files not downloading. What am I doing wrong? - javascript

I need to download a file that has been uploaded to Firebase Storage. I used to get the CORS error but I used gsutil as per the docs and now nothing really happens. The download doesn't start. What am I doing wrong?
//Create reference of Storage
let storage = firebase.storage();
//Create reference to file in Storage
let pathSubmission = 'Uploads/' + this.place1+ '/' + this.place2+ '/' + this.place3+ '/' + this.downloadSubmissionUID;
let storageRef = storage.ref(pathSubmission);
storageRef.getDownloadURL().then((url) => {
console.log('downloadURL arg: ' + url);
//Download file (no idea how this works)
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
})

Figured it out using Kai's blog, here's my solution:
//Create reference of Storage
let storage = firebase.storage();
//Create reference to file in Storage
let pathSubmission = 'Uploads/' + this.place1 + '/' + this.place2+ '/' + this.place3 + '/' + this.downloadSubmissionUID;
//Assign Storage reference the path reference
let storageRef = storage.ref(pathSubmission);
//Download file by creating XMLHttpRequest
storageRef.getDownloadURL().then((url) => {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
//Create an `a` tag (since it has an `href` and a `download` attribute)
var a = document.createElement('a');
a.href = window.URL.createObjectURL(xhr.response);
a.download = 'someFileName';
a.style.display = 'none';
document.body.appendChild(a);
a.click(); //Simulates a click event
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
})

Actually, the file is probably getting downloaded. The problem is in this code here:
//Download file (no idea how this works)
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function(event) {
var blob = xhr.response;
};
xhr.open('GET', url);
xhr.send();
The browser is doing exactly what it's told. It downloads the file as a blob. Then discards the result, because you aren't doing anything with the data in the onload handler!
First, I'd recommend using fetch, which provides a much more intuitive API than the XMLHttpRequest (in my opinion).
Here's how you might do that:
// Create reference of Storage
let storage = firebase.storage();
// Create reference to file in Storage
let pathSubmission = 'Uploads/' + this.place1+ '/' + this.place2+ '/' + this.place3+ '/' + this.downloadSubmissionUID;
let storageRef = storage.ref(pathSubmission);
storageRef.getDownloadURL()
.then(url => {
console.log('Download URL: ' + url)
return fetch(url)
})
.then(response => response.blob())
.then(blob => {
console.log('File downloaded as blob')
// Do something with the blob...
})
.catch(error => {
console.error('Something went wrong while downloading the file:', error)
})

Related

Google Drive API browser upload PDF empty

I can upload the pdf file, but it will upload as blank/empty file. I don't know what am i missing from here.
Backend i receive the file, i also tried without converting to Base64 and still the same thing.
using (var sr = new StreamReader(file.OpenReadStream(), System.Text.Encoding.UTF8))
{
_fContent = await sr.ReadToEndAsync();
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(_fContent);
_fContent = System.Convert.ToBase64String(plainTextBytes);
}
Frontend i create the request.
endpoint = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id';
method = 'POST';
_metadata = {
'name': fileName,
'mimeType': 'application/pdf',
'parents': [zzzz]
};
//blob is the data we receive in backend from _fContent variable
var file = new Blob([blob], { type: 'application/pdf' });
var accessToken = gapi.auth.getToken().access_token;
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(_metadata)], { type: 'application/json' }));
form.append('file', file);
var xhr = new XMLHttpRequest();
xhr.open(method, endpoint);
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xhr.responseType = 'json';
xhr.onload = () => {
GapiUploadCallback(xhr.response, responseReturn);
};
xhr.send(form);
This is what i receive in google drive API, an empty/blank PDF file. Note: File size is
1 MB (1,424,457 bytes)
Using javascript FileReader was the only option i could solve this. PDF's are rendering ok on Google Drive now.
var fileUpload = $("#fileUpload").get(0);
var files = fileUpload.files;
var reader = new FileReader();
reader.onload = function () {
var dataURL = reader.result;
var file = new Blob([dataURL], { type: 'application/pdf' });
var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token.
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(_metadata)], { type: 'application/json' }));
form.append('file', file);
var xhr = new XMLHttpRequest();
xhr.open(method, endpoint);
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xhr.responseType = 'json';
xhr.onload = () => {
GapiUploadCallback(xhr.response, responseReturn);
};
xhr.send(form);
};
reader.readAsArrayBuffer(files[0]);

Write Byte Array to a file JavaScript

I have Java REST webservice that returns documents as byte array, I need to write JavaScript code to get the webservice's response and write it to a file in order to download that file as PDF Kindly see a screen shot of the webservice's response and see my sample code this code downloads a corrupted PDF file.
var data = new FormData();
data.append('PARAM1', 'Value1');
data.append('PARAM2', 'Value2');
var xhr = new XMLHttpRequest();
xhr.open('POST', 'SERVICEURL');
xhr.withCredentials = true;
xhr.setRequestHeader("Authorization", "Basic " + btoa("username:password"));
xhr.onload = function() {
console.log('Response text = ' + xhr.responseText);
console.log('Returned status = ' + xhr.status);
var arr = [];
arr.push(xhr.responseText);
var byteArray = new Uint8Array(arr);
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(new Blob(byteArray, { type: 'application/octet-stream' }));
a.download = "tst.pdf";
// Append anchor to body.
document.body.appendChild(a)
a.click();
// Remove anchor from body
document.body.removeChild(a)
};
xhr.send(data);
Since you are requesting a binary file you need to tell XHR about that otherwise it will use the default "text" (UTF-8) encoding that will interpret pdf as text and will mess up the encoding. Just assign responseType property a value of 'blob' or the MIME type of pdf
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob'; // tell XHR that the response will be a pdf file
// OR xhr.responseType = 'application/pdf'; if above doesn't work
And you will access it using response property and not responseText.
So you will use arr.push(xhr.response); and it will return you a Blob.
If this doesn't work, inform me will update another solution.
Update:
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob'; // tell XHR that the response will be a pdf file
xhr.onload = function() {
var blob = this.response;
var a = window.document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = "tst.pdf";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};

Handling stream from server and displaying the file in browser

I'm trying to find the global solution for handling strem files from the server.
My response object is angular http Response
My object has differnet _body for images/docs etc.
images is a long string of ##$.. and docs are json
I've been looking for solutions over the web, and got to implement this:
let contentType = resData.headers.get("Content-Type");
let blob = new Blob([resData.arrayBuffer()], {type: contentType});
let url = window.URL.createObjectURL(blob);
window.open('url: ', url);
This code downloads a file that has content-type of octet-stream
but all other files are not displayed in the browser.
My main goal is to have the same behavior if would have put in the URL the API that returns a stream, and the browser knows how to handle it (images are shown in browser, files that browser doesn't support are automatically downloaded etc.)
This is the code for the request.
return Observable.create(observer => {
let xhr = new XMLHttpRequest();
xhr.open(Object.keys(RequestMethod).find(k => RequestMethod[k] === url.method), url.url, true);
const shift =
xhr.setRequestHeader('Content-type', 'application/json');
xhr.responseType = (Object.keys(ResponseContentType).find(k => ResponseContentType[k] === url.responseType)).toLocaleLowerCase();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let blob = new Blob([xhr.response], {type: body.MimeType});
observer.next({blob: blob, fileName: body.FileName});
observer.complete();
} else {
observer.error(xhr.response);
}
}
}
xhr.send(url.getBody());
This is the code for the special handling of each mimeType
handleAttachmentItem(resData) {
let blob = resData.blob;
const fileName = resData.fileName;
if (blob.type.includes('image')) {
let b64Response = window.URL.createObjectURL(blob);
let outputImg = document.createElement('img');
outputImg.src = b64Response;
let w = window.open();
w.document.write('<html><head><title>Preview</title><body style="background: #0e0e0e">');
w.document.write(outputImg.outerHTML);
} else if (blob.type.includes('text')) {
let url = window.URL.createObjectURL(blob);
window.open(url);
} else {
let a = document.createElement("a");
document.body.appendChild(a);
let url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}
}

JS FileSaver non interactive

I have an array of file to save by using a loop and i generate the name of each file. I want to save file directly (in non interactive way) without asking me to confirm. How can i do ?
Here is my code for saving file
var url = img.src.replace(/^data:image\/[^;]/, 'data:application/octet-stream');
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob'; //Set the response type to blob so xhr.response returns a blob
xhr.open('GET', url , true);
xhr.onreadystatechange = function () {
if (xhr.readyState == xhr.DONE) {
var filesaver = require('file-saver');
filesaver.saveAs(xhr.response, nameFile); // nameFile the name of file to be saved
}
};
xhr.send(); //Request is sent
Finally, i find a solution, instead of saving file, i write it by creating a new one.
for (var i = 0; i < tabForm.length; i++) {
var imgData = $('#affichageqr')[0].childNodes[1].childNodes[0].src;
var data = imgData.replace(/^data:image\/\w+;base64,/, '');
fs.writeFile(qrcode.getNomQRCode()+'.jpeg', data, {encoding: 'base64'}, function(err){
if (err) {
console.log('err', err);
}
console.log('success');
});
}

How to download files using javascript asynchronously?

I'm building a Chrome extension to download a series of files from a site. The downloading function is derived from How to save a file from a URL with JavaScript.
The program structure is like:
function download()
{
while(there are still files to download)
{
saveFile(url);
}
}
But I find that all the files are actually written to disk at once after download() returns. And the addresses of those files start with blob: when examine from Chrome's downloads manager.
I wonder if I make the call to saveFile asynchronously, those files could be written one at a time.
Using promises, which are available in Chrome out of the box, you can define the functions like so:
// Download a file form a url.
function saveFile(url) {
return new Promise(function(resolve, reject) {
// Get file name from url.
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function() {
resolve(xhr);
};
xhr.onerror = reject;
xhr.open('GET', url);
xhr.send();
}).then(function(xhr) {
var filename = url.substring(url.lastIndexOf("/") + 1).split("?")[0];
var a = document.createElement('a');
a.href = window.URL.createObjectURL(xhr.response); // xhr.response is a blob
a.download = filename; // Set the file name.
a.style.display = 'none';
document.body.appendChild(a);
a.click();
return xhr;
});
}
function download(urls) {
return Promise.all(urls.map(saveFile));
}
Using it:
download.then(function() {
alert("all files downloaded");
}).catch(function(e) {
alert("something went wrong: " + e);
});
If you want to wait for 1 file to download before proceeding with next, the download function should be written like:
function download(urls) {
var cur = Promise.resolve();
urls.forEach(function(url) {
cur = cur.then(function() {
return saveFile(url);
});
});
return cur;
}
Usage is same as before.

Categories