I'm downloading a file from a server. There I'm setting a file name which I would like to read on a frontend. Here is how I'm doing it on server:
string fileName = "SomeText" + DateTime.UtcNow.ToString() + ".csv";
return File(stream, "text/csv", fileName);
Basically I will return different types of files, sometimes csv sometimes xlsxand sometimes pdf. So I want to get filename on a frontend so I might save it as it should be for example SomeText.pdf it will be saved automatically on local machine as pdf.
Here is my frontend code:
getFileFromServer(params).then(response => {
console.log('server response download:', response);
const type = response.headers['content-type'];
const blob = new Blob([response.data], { type: type, encoding: 'UTF-8' });
//const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'file.xlsx'; // Here I want to get rid of hardcoded value instead I want filename from server
link.click();
link.remove(); // Probably needed to remove html element after downloading?
});
I saw in Network tab under Response Headers that there is a Content-Disposition which holds that info:
Content-Disposition: attachment; filename="SomeText22/08/2019 10:42:04.csv";
But I don't know is it available in my response and how can I read it in my frontend part so I might replace
link.download = 'file.xlsx';
with Path from a server ..
Thanks a lot guys
Cheers
this is a special type of header so to get this header in frontend the backend person should allow from his end. then you can the headers in the response
response.headers.get("content-disposition")
the below code I have used in my project
downloadReport(url, data) {
fetch("url of api")
.then(async response => {
const filename = response.headers
.get("content-disposition")
.split('"')[1];
const text = await response.text();
return { filename, text };
})
.then(responseText => this.download(responseText))
.catch(error => {
messageNotification("error", error.message);
throw new Error(error.message);
});
}
You can get filename from Content-Disposition header by this way
getFileFromServer(params).then(response => {
// your old code
const [, filename] = response.headers['content-disposition'].split('filename=');
const link = document.createElement('a');
link.download = filename;
// your old code
});
Hope this will help
To get the filename from Content-Disposition on client side(frontend) you must give permission from server side(Backend). Spring users can use
#CrossOrigin(value = {"*"}, exposedHeaders = {"Content-Disposition"})
#Controller
#RequestMapping("some endpoint")
in their controller class.
Then from frontend we can get filename using.
const filename = response.headers['content-disposition'].split('filename=')[1];
Hope this helps. Above solution worked fine for me
Related
I got Node JS server which gets XLSX file contents from metabase:
app.get('/channels', async (req, res) => {
// make request to metabase and take response as XLSX
const queryRequestURL = `${api}/public/card/${cardId}/query/xlsx?parameters=${params}`;
const result = got(queryRequestURL);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader("Content-Disposition", "attachment; filename=file.xlsx");
return res.send(res);
});
It returns file contents like
So when i make request to server and receive response - it comes as file contents above.
I need to download this data as ordinary excel file on browser side.
What i've tried:
// make request with typical fetch and get result to res variable.
const filename = 'file.xlsx';
const file = new File(res, filename ,{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
// create link and click it virtually to download created file
var a = document.createElement('a');
a.href = window.URL.createObjectURL(file);
a.download = filename;
a.click();
But I'm getting the error:
Uncaught (in promise) TypeError: Failed to construct 'Blob': The provided value cannot be converted to a sequence.
I think that I'm doing something wrong and there is more simple way to download file.
Without seeing how you're fetching, it's hard to know. But you should be able to use response.blob() to download the result.
fetch("${api}/channels}", {
method: "GET",
})
.then((response) => response.blob())
.then((blob) => {
var url = window.URL.createObjectURL(blob);
var a = document.createElement("a");
a.href = url;
a.download = "file.xlsx";
document.body.appendChild(a);
a.click();
a.remove();
});
As Joey Ciechanowicz mentioned, we should return response.buffer() from backend and work with its data as blob at frontend.
I mean
NodeJS side (using Got):
const result = got(queryRequestURL, {
headers: headers
});
return await result.buffer()
Frontend side (pure JavaScript):
// fetch data
const result = await fetch(api + path);
return result.blob();
// download file
const filename = 'export.xlsx';
var url = window.URL.createObjectURL(result);
var a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
a.remove();
I am trying to download the image and save it in my server from the url address. So for example I make a POST request with URL of the image. I download the image and I save it in my server. The problem comes when I need to figure our the extension of the image. Right now it works staticaly only for jpg files, but it should work for png aswell. How can I find out the extension of the file before saving it?
One way would be to get the extension from the url itself, but not all urls will have the extension , for example: https://media.istockphoto.com/photos/winter-in-the-sequoias-picture-id1292624259
This is the code that I have made right now. It works, however how I said, its static and only working for jpg:
var config = {
responseType: 'stream'
};
async function getImage(url) {
let time = Math.floor(Date.now() / 1000)
let resp = await axios.get(url, config)
resp.data.pipe(fs.createWriteStream(time+'.jpg')) // here I need to get the image extension isntead of static '.jpg'
}
You can use response headers for that. The Content-Type header should tell you the type of the file and with Content-Disposition you can get the filename with extension.
In your code you can access these headers like this
resp.headers['content-type'];
resp.headers['content-disposition'];
I'd suggest using a module such as mime to get the extension from the content-type.
Complete example:
const axios = require('axios');
const fs = require('fs');
const mime = require('mime');
var config = {
responseType: 'stream'
};
async function getImage(url) {
let time = Math.floor(Date.now() / 1000)
let resp = await axios.get(url, config)
const contentLength = resp.headers['content-length'];
const contentType = resp.headers['content-type'];
const extension = mime.extension(contentType);
console.log(`Content type: ${contentType}`);
console.log(`Extension: ${extension}`);
const fileName = time + "." + extension;
console.log(`Writing ${contentLength} bytes to file ${fileName}`);
resp.data.pipe(fs.createWriteStream(fileName));
}
const url = 'https://media.istockphoto.com/photos/winter-in-the-sequoias-picture-id1292624259';
getImage(url)
This will give an output somewhat like:
Content type: image/jpeg
Extension: jpeg
Writing 544353 bytes to file 1638867349.jpeg
From the backend api I am getting a list of students data into a valid excel file which is being downloaded on hitting the endpoint /api/v1.0/students/students-xlsx/ But on the client side when I am calling this endpoint it's showing unreadable format and being downloaded as a corrupt excel file.
I followed some stackoverflow suggestions like atob, encodeURI the response data and add specific type (UTF-8) but it failed. Still I am getting the corrupt file with weird characters.
excelFileDownload() {
this.$http.get(this.exportXLUrl)
.then((response) => {
response.blob().then(() => {
const blob = new Blob([response.body], { type: response.headers.get('content-type') });
const filename = response.headers.map['content-disposition'][0].split('filename=')[1];
const link = document.getElementById('download-excel-file');
link.href = window.URL.createObjectURL(blob);
link.download = filename.split('"').join('');
link.style.display = 'block';
link.click();
});
});
},
I expect the output as same as when I am just using browsable API to call the endpoint- which is giving me the appropriate xls format file with readable characters. But on the client side I am not getting that at all. It's all broken. Any help would be appreciated to improve my code.
If you're willing to use XMLHttpRequest
(untested)
const xhr = new XMLHttpRequest();
xhr.open('GET', this.exportXLUrl, true);
xhr.responseType = 'blob';
xhr.addEventListener('load', () =>
{
if(xhr.status == 200)
{
const url = window.URL.createObjectURL(xhr.response);
const contentDisposition = xhr.getResponseHeader('content-disposition');
const filename = /filename=([^;]*)/.exec(contentDisposition)[1];
const link = document.getElementById('download-excel-file');
link.href = window.URL.createObjectURL(blob);
link.download = filename.split('"').join('');
link.style.display = 'block';
link.click();
//Dont forget to revoke it
window.URL.revokeObjectURL(url);
}
else
{
//error
}
});
xhr.addEventListener('error', err =>
{
//error
});
xhr.send();
I need to pass the Content-type in headers and responseType with the get request as below:
headers: { 'Content-Type': 'application/vnd.openxmlformatsofficedocument.spreadsheetml.sheet' },
responseType: 'arraybuffer'
It works fine now.
I have a link /api/ticket/1/download to my server and if I copy paste it into the browser, it will start a download of my file with it's filename extension and everything set up already.
How can I do it in javascript? With thymeleaf i was just using <a href="myLink"> and it worked like if I paste it in the browser. But due to react-router a href just goes to another page.
But if I use await fetch like this:
let url1 = '/api/ticket/' + fileId + '/download';
const response = await fetch(url1);
const blob = response.blob();
const url = window.URL.createObjectURL(new Blob([blob]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `file.jpg`);
document.body.appendChild(link);
link.click();
i can transfer response into blob, but then i miss the files name and extension.
The only way to solve this that i found is: send file name and extension in a response header and than parse them manualy.
like:
in spring:
myControllerMethod( HttpServletResponse response){
...
response.setContentType(att.getType());
response.setHeader("Content-Disposition", "attachment; filename=\"" + myFileName + "\"");
}
in react:
const headers = response.headers;
const file = headers.get("Content-Disposition");
and then parse manully this 'file' to get filenamel;
Try in this way, it may help you :
fetch('http://localhost:8080/employees/download')
.then(response => {
response.blob().then(blob => {
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = 'employees.json';
a.click();
});
For more reference you can go through This link
I am working on a client-server application, and at the moment i am returning a file result in my web api controller, like this:
FileContentResult result = new FileContentResult(System.IO.File.ReadAllBytes(docDestination), "application/msword")
{
FileDownloadName = "myFile.docx"
};
return result;
on my client side i receive the response like this:
i thaught at begin that the browser detects the file automaticly and fires the download dialog, but no, so i tried to treat the result as a blob like this:
.then(response => {
console.log("here lives the response:", response);
var headers = response.headers;
var blob = new Blob([response.bodyText], { type: headers['application/msword'] });
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "Filename";
link.click();
the download fires, but the content is not identified as docx file, and his content is broken.
Any tip on how to do it?
Thanks