Get binary data with XMLHttpRequest in a Firefox extension with AXIOS - javascript

I am trying to download binary data from Firefox browser. My app is based on react and redux and using axios as my HTTP client.
I have to send
xhr.open() before responseType= arraybuffer
Below implementation didnt work
axios({
url:`http://api.demo6.test.com:8080/resources/v1/${dObj.payload.surveyId}`,
responseType: 'arraybuffer',
method: 'post',
data: dObj.payload.filterData,
headers: {
"Accept": "application/vnd.ms-excel",
"Content-Type": "application/json",
"X-Bazaarify-Session-Token": "cfff-7-07f13399abed" //token
}
}).then(function(response) {
// const y = yield put(surveyResponseDowloadComplete({
// data: response.data
// }));
let blob = new Blob([response.data], {type: "application/vnd.ms-excel"});
let link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = "test.xls";
link.click();
console.log(response);
//response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
How can I do it with axios?

This is a simple saga that downloads binary data using axios:
function * downloadFileSaga(url) {
const response = yield axios.get(url, {
responseType: 'arraybuffer'
})
console.log(response.data); // arraybuffer
}
You can then use Blob or FileReader to work with the arraybuffer further.
Example: https://codesandbox.io/s/n7kmvjr49m

Related

How to download a ReadableStream on the browser that has been returned from fetch

I am receiving a ReadableStream from a server, returned from my fetch call.
A ReadableStream is returned but I don't know how to trigger a download from this stage. I can't use the url in an href because it requires an Authorization token.
I don't want to install fs on the client so what options do I have?
try {
const res = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/octet-stream'
}
});
const blob = await res.blob();
const newBlob = new Blob([blob]);
const newUrl = window.URL.createObjectURL(newBlob);
const link = document.createElement('a');
link.href = newUrl;
link.setAttribute('download', 'filename');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
window.URL.revokeObjectURL(newBlob);
} catch (error) {
console.log(error);
}
Update 1
I converted the file to a Blob, then passed it into a newly generated href. Successfully downloaded a file. The end result was the ReadStream contents as a .txt file.
Meaning stuff like this
x:ÚêÒÓ%¶âÜTb∞\܃
I have found 2 solutions, both worked but I was missing a simple addition to make them work.
The native solution is
try {
const res = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`
}
});
const blob = await res.blob();
const newBlob = new Blob([blob]);
const blobUrl = window.URL.createObjectURL(newBlob);
const link = document.createElement('a');
link.href = blobUrl;
link.setAttribute('download', `${filename}.${extension}`);
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
// clean up Url
window.URL.revokeObjectURL(blobUrl);
This version is using the npm package steamSaver for anyone who would prefer it.
try {
const res = await fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`
}
});
const fileStream = streamSaver.createWriteStream(`${filename}.${extension}`);
const writer = fileStream.getWriter();
const reader = res.body.getReader();
const pump = () => reader.read()
.then(({ value, done }) => {
if (done) writer.close();
else {
writer.write(value);
return writer.ready.then(pump);
}
});
await pump()
.then(() => console.log('Closed the stream, Done writing'))
.catch(err => console.log(err));
The key for why it was not working was because I did not include the extension, so it either errored out because of the mimetype was wrong or it opens a .txt file with a string of the body instead of the image.

Download file in react, sent by express

None of the other solutions I have read on SO have worked so far, so please bear with me.
I have a frontend in React, where the user sends a request for a file download, along with some variables, to the backend:
const data = text.value;
fetch("http://localhost:4000/dl", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data,
file: filetypes.selected,
}),
}).then((res) => {
...???...
});
the data variable is some JSON, like so: {"arrowParens": "always"}.
the file variable contains the filename to be downloaded: .prettierrc.
My backend is in NodeJs/express, and handles the request like so:
index.ts:
app.use("/dl", getFile);
getFile.ts:
import appRoot from "app-root-path";
const index = async (req: Request, res: Response) => {
const { data, file } = req.body;
// download file if folder exists
if (fs.existsSync(`${appRoot}/tmp/${file}`)) {
res.sendFile(
".prettierrc",
{
dotfiles: "allow",
headers: {
"Content-Type": "json",
},
root: path.join(__dirname, "../tmp"),
}
);
}
};
I'm getting the correct response back from the server: POST /dl 200 11.878 ms - 55 but the file isn't downloading, so I think I have to have some extra code. I've read another post where OP created an anchor tag, and set the href to the blob url, but this doesn't seem to work since the response doesn't show me a URL. I had to opt for the POST request since I have to send those file-determining variables. Not sure if method makes a difference here.
How do I get the browser to prompt the download of my .prettierrc file? What am I missing?
You have to create an anchor tag with href and trigger the click.
something like this
fetch("http://localhost:4000/dl", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
data,
file: filetypes.selected,
}),
}).then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = id;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})

Axios error Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream

I'm getting this error when trying to do a POST request using axios:
Error: Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream at createError
Here's my request:
async function fetchAndHandleErrors() {
const url = `/claim/${claimId}`;
const headers = {
Accept: 'application/json',
Authorization: `Bearer ${token}`,
};
const body = new FormData();
body.append('damage_description', damageDescription);
body.append('damaged_phone', {
uri: imageUri,
type: 'image/jpeg', // or photo.type
name: imageUri,
});
const result = await axios({
'post',
url: `${baseUrl}${url}`,
data: body,
headers,
});
return result.data;
}
I tried removing result.data and still get the same error. Why is that?
If you eventually still need a solution for this, I managed to get rid of this error by using the formData.pipe() method. For your case, it could look like this :
import axios from 'axios'
import concat from 'concat-stream'
import fs from 'fs'
import FormData from 'form-data'
async function fetchAndHandleErrors() {
const file = fs.createReadStream(imageUri)
let body = new FormData();
body.append('damage_description', damageDescription);
body.append('damaged_phone', file);
body.pipe(concat(data => {
const url = `/claim/${claimId}`;
const headers = {
'Authorization': `Bearer ${token}`,
...body.getHeaders()
};
const result = await axios({
'post',
url: `${baseUrl}${url}`,
data: body,
headers,
});
return result.data;
}))
}
Please let me know if you still encounters your issue, I'll be glad to help !

How to download ZIP file from reactjs using post api?

How to download zip file from reactjs using POST API.
The request is coming from nodejs in binary form
you can use jszip link https://github.com/Stuk/jszip like
import zipTargetFiles from '/path'
zipTargetFiles( data ).then(file => {
//operations
})
if you use fetch like this.
fetch('URL', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
//Body
})
}).then((response)=>{
//here is youu want zip data
var zip = new JSZip();
var zipData = response.data //
// Add an top-level, arbitrary text file with contents
zip.file("response.txt", zipData);
// Generate the zip file asynchronously
zip.generateAsync({type:"blob"})
.then(function(content) {
// Force down of the Zip file
saveAs(content, "zipFile.zip");
});
}).catch((error)=>{
console.log(error)
})
You can use JsZip on Client Side. Then, do a request with axios. Like this:
request = (currentUrl: string): Promise<void> => axios({
url: currentUrl,
method: 'GET',
responseType: 'blob',
}).then((response) => {
const url: string = window.URL.createObjectURL(new Blob([response.data]));
});

Download PDF from http response with Axios

I am working on a Vue application with a Laravel back-end API. After clicking on a link I would like to do a call to the server to download a certain file (most of the time a PDF file). When I do a get request with axios I get a PDF in return, in the body of the response. I would like to download that file directly.
To give you a better view of how the response is looking like:
(note: I know a real text response is better than an image but I don't see any way to return that because of the length of the actual PDF content..)
Is there any way of downloading that file with JavaScript or something? It has to be specific a direct download without clicking on the button again.
Code
// This method gets called when clicking on a link
downloadFile(id) {
const specificationId = this.$route.params.specificationId;
axios
.get(`${this.$API_URL}/api/v1/suppliersmanagement/product-specifications/${specificationId}/fileupload/${id}/download`, {
headers: this.headers,
})
.then(response => {
console.log(response);
// Direct download the file here..
})
.catch(error => console.log(error));
},
As #Sandip Nirmal suggested I've used downloadjs and that worked out pretty good! Had to make a few adjustments to my code but in the end it worked out.
My new code
// npm i downloadjs
import download from 'downloadjs'
// method
downloadFile(file) {
const specificationId = this.$route.params.specificationId;
axios
.get(`${this.$API_URL}/api/v1/suppliersmanagement/product-specifications/${specificationId}/fileupload/${file.id}/download`, {
headers: this.headers,
responseType: 'blob', // had to add this one here
})
.then(response => {
const content = response.headers['content-type'];
download(response.data, file.file_name, content)
})
.catch(error => console.log(error));
},
You should use 'responseType' option. For example:
axios.get(
url,
{responseType: 'blob'} // !!!
).then((response) => {
window.open(URL.createObjectURL(response.data));
})
You have 2 options for this. If you want to do it from server and if you are using Node.js as a backend. You can do it easily using res.download method of express. You can follow this answer for that Download a file from NodeJS Server using Express.
But if you want to handle it from client then there are few options since you can't use axios, XHR, fetch to download file directly. You can either use download.js or write your own code in following way.
return axios({
url: '/download', // download url
method: 'get',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
mode: 'no-cors'
}
})
.then(response => response.blob())
.then(blob => {
var url = window.URL.createObjectURL(blob)
var a = document.createElement('a')
a.href = url
a.download = fileName
a.click()
a.remove()
setTimeout(() => window.URL.revokeObjectURL(url), 100)
})
Since response returned from server is in json format you need to convert it into ObjectURL and set it to anchor tag.
If you sneak inside download.js code you will find same implementation.
2022 answer: using node.js, fs.promises and async/await
The key is using responseType: 'stream' per the Axios docs.
import axios from 'axios';
import { writeFile } from 'fs/promises';
const downloadFile = async () => {
const response = await axios.get('https://someurl', {
params: {
// ...
},
// See https://axios-http.com/docs/api_intro
responseType: 'stream',
});
const pdfContents = response.data;
await writeFile('file.pdf', pdfContents);
};
You can do it like this
download(filename) {
fetch(url , { headers })
.then(response => response.blob())
.then(blob => URL.createObjectURL(blob))
.then(uril => {
var link = document.createElement("a");
link.href = uril;
link.download = filename + ".csv";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
}
here I want to download a CSV file, So I add .csv to the filename.
const downloadPDF = (id, fileName) => {
axios({
method: 'get',
url: `https://api.example.com/pdf/invoice/${id}`,
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'Content-Type': 'application/json'
},
responseType: 'blob'
}).then(function (response) {
const a = document.createElement('a');
a.href = window.URL.createObjectURL(response.data);
a.download = `${fileName}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
});
}

Categories