Download file in ReactJS app preserving the original filename - reactjs

I'm serving pdf file with nodejs/koa2
ctx.body = readableStream;
ctx.attachment('file.pdf');
The file successfully arrives and on the client side i receive it with ReactJS application:
const document = useSelector(selectors.getFile(documentFile.key));
if (document) {
window.open(window.URL.createObjectURL(new Blob([document], { type: "application/octet-stream" })), "_self");
}
...
const openFile = useCallback((key) => {
dispatch(actions.getFile.request(key))
}, [dispatch]);
It successfully downloads the file, but completely ignores response header Content-Disposition: attachment; filename="file.pdf" and downloads it under the name like d3aa7870-bd35-4645-a926-294392343cfc which is taken from the BLOB url: Request URL: blob:http://localhost:3000/d3aa7870-bd35-4645-a926-294392343cfc.
Could you please advise how to correctly save it under the name of file.pdf on the client side?

just create an element and set download attribute with file name
const document = useSelector(selectors.getFile(documentFile.key));
if (document) {
const url =window.URL.createObjectURL(new Blob([document], { type: "application/octet-stream" }))
const a = document.createElement("a");
a.style = "display: none";
document.body.appendChild(a);
a.href = url;
a.download = "fileName";
a.click();
window.URL.revokeObjectURL(url);
}

Related

Download zip file from http api in react, Receiving error: "Unable to expand file.zip. It is an unsupported format"

Thanks in advance for taking a look. I am working on being able to download a zip file from react through a django api request. I am able to click on my pop up that downloads the zip file, but when I double click on the zip file to open, I get this error: "Unable to expand file_name.zip. It is an unsupported format" My response with the zip file seems to be passing correctly to the front end, so I am thinking it may be something wrong with the react code when making the "blob"? Thanks again.
Django code:
class DownloadZip(APIView):
def post(self, request, format=None):
# information to find file to create zip
profile_name = request.data["profile"]
profile_year = request.data["year"]
# file path to create zips from
path = str(Path(__file__).parent.resolve())
zip_dir = shutil.make_archive(profile_name + profile_year, "zip", path + "/" + profile_name + profile_year)
s = io.StringIO(zip_dir)
response = HttpResponse(s, content_type = "application/zip")
zip_name = profile_name + profile_year + ".zip"
response["Content-Disposition"] = f"attachment; filename={zip_name}"
return response
React code:
downloadZip = async () => {
const params = {
profile: this.state.profileName,
year: this.state.year,
};
axios({
url: `${serverUrl}/download_zip`,
method: "post",
data: params
}).then(
(res) => {
const url = window.URL.createObjectURL(new Blob([res.data],{type:'application/zip'}));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.zip');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
},
(error) => {
console.log(error);
});
}
I did do a fellow commentor's suggestion, and updated to get route with query params, but am having the same issue. I can double click on the zip link on the web browser but a pop up appears "Unable to expand filename.zip. It is an unsupported format"
Please try adding {responseType: 'arraybuffer'}. I also had the same problem but after adding this {responseType: 'arraybuffer'}. I am getting correct file.
downloadZip = async () => {
const params = {
profile: this.state.profileName,
year: this.state.year,
};
axios.post(
`${serverUrl}/download_zip`,
params,
{
responseType: 'arraybuffer'
}
).then(
(res) => {
const url = window.URL.createObjectURL(new Blob([res.data],{type:'application/zip'}));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.zip');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
},
(error) => {
console.log(error);
});
}

Download a zip file in reactjs without any plugins

I am getting a response from a rest api whose Content-Type is text/html and Content-Encoding is gzip.
I tried to download it using blob
const blob = new Blob([res], { type: 'application/gzip' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
document.body.appendChild(a);
a.click();
But the downloaded gz file was not able to open (seems to be corrupted).
can some one help me with downloading zip file in reactjs.
Edit: require a solution without using any external plugins
you can use jszip npm module.
For example:
var zip = new JSZip();
zip.file("Hello.txt", "Hello World\n");
var img = zip.folder("images"); //images is the folder which will be zip
img.file("smile.gif", imgData, {base64: true});
zip.generateAsync({type:"blob"}).then(function(content) {
saveAs(content, "example.zip");
});
To use it without jszip, you can try the following code:
function str2bytes (str) {
var bytes = new Uint8Array(str.length);
for (var i=0; i<str.length; i++) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
}
and its usage:
var blob = new Blob([str2bytes(myData)], {type: "application/zip"});
saveAs(blob, "data.zip");
But jszip is a better alternative approach.

Unable to open PDF while converting it from a HTML in react

I am getting an html file from a backend application and now saving it in pdf format in react. However, unable to open it in adobe :(
CreateFile(data, contentType) {
let file;
if (contentType === "text/html") {
// file = new Blob([data], { type: contentType });
file = new Blob([new Uint8Array(data)], { type: contentType });
}
saveDocument() {
let contentType = "application/pdf";
let file = this.createFile(data,
contentType.toLowerCase());
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else { // others apart from Safari and Opera mini
var a = document.createElement("a"),
url = window.URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
can anyone help?
I suggest you using a library like wkhtmltopdf in your backend to convert the html into pdf before sending it.

Download .xls file via POST

I am using AngularJS and in POST request download file .xls
this.$http.post('/api/rept/detc/xls', params).then((response) => {
let blob = new Blob([response], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
}),
objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
})
But then download file without format with name (in this format: c65c45f8-e6a3-458d-xxxx-43c5fcxxxxx).
How can I download without FileSave package?
Similar approach that worked for me:
this.$http.post('/api/rept/detc/xls', params).then((response) => {
let blob = new Blob([response]);
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = 'filename_here';
a.target = '_blank';
a.click();
})

How to download files using axios.post from webapi

I have a complex object parameter that I need to send as post, as it could be too long for querystring. The post call is asking to have an excel file dynamically generated and then downloaded asynchronously. But all of this is happening inside of a react application. How does one do this using axios.post, react, and webapi? I have confirmed that the file does generate and the download up to the response does come back, but I'm not sure how to actually open the file. I have a hidden iframe that I'm trying to set the path, src, of the file to, but I dont know what response property to use.
// webapi
[HttpPost]
public HttpResponseMessage Post([FromBody]ExcelExportModel pModel)
{
var lFile = ProductDataModel.GetHoldingsExport(pModel);
var lResult = new HttpResponseMessage(HttpStatusCode.OK);
lResult.Content = new ByteArrayContent(lFile);
lResult.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "HoldingsGridExport.xls"
};
lResult.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return lResult;
}
// client side api
static getHoldingsExport({ UserConfigurationID, UserID, Configurations, ViewName, SortModel, FilterModel, UserConfigType, IsDefault, LastPortfolioSearchID = null, ProductId }) {
const filterModel = JSON.stringify(FilterModel); // saving as string as this model is dynamically generated by grid out of my control
const sortModel = JSON.stringify(SortModel);
let params = JSON.stringify({
UserConfigurationID,
UserID,
Configurations,
ViewName,
filterModel,
sortModel,
UserConfigType,
IsDefault,
LastPortfolioSearchID,
ProductId
});
return axiosInstance.post("/api/HoldingsExport", params);
}
// client side app call to get file
HoldingsApi.getHoldingsExport(config)
.then(function(response) {
debugger;
let test = response;
})
.catch(error => {
toastr.success('Failed to get export.');
});
This is how I've achieved file downloads by POSTing via Axios:
Axios.post("YOUR API URI", {
// include your additional POSTed data here
responseType: "blob"
}).then((response) => {
let blob = new Blob([response.data], { type: extractContentType(response) }),
downloadUrl = window.URL.createObjectURL(blob),
filename = "",
disposition = response.headers["content-disposition"];
if (disposition && disposition.indexOf("attachment") !== -1) {
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, "");
}
}
let a = document.createElement("a");
if (typeof a.download === "undefined") {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
}).catch((error) => {
// ...
});
Just in case the above solution does not serve you quite well, here is how I could be able to download videos that are hosted on S3 AWS buckets,
const handleDownload = () => {
const link = document.createElement("a");
link.target = "_blank";
link.download = "YOUR_FILE_NAME"
axios
.get(url, {
responseType: "blob",
})
.then((res) => {
link.href = URL.createObjectURL(
new Blob([res.data], { type: "video/mp4" })
);
link.click();
});
};
And I trigger handleDownload function in a button with onClick.
The url in the function has the video URL from S3 buckets

Resources