Reputation: 151
I am trying to make an onClick button to download a file from S3 bucket using pre-signet url. The problem comes when I received my url. I want an automatic redirect or kind of. In other words, how can I lunch the download file after getting back my signed url?
The onClick event is on the Download button.
Redux action call my nodejs route api route nodejs
Ask for pre-signed url then send it to my redux reducer.
Now in my front-end page, I got my link but I want an automatic redirect to start the file download. Part of Component
Hope my first post isn't too messy.
Upvotes: 5
Views: 19210
Reputation: 4679
There is a much simpler way of making this work. You just need to set the Content-Disposition header in the presigned url. When set to "attachment", this header field tells the browser to download the file rather than trying to display it in the browser. If the presigned url contains this header you can simply navigate to that url and it'll initiate the download.
In my backend (C#) this is done like this:
var request = new GetPreSignedUrlRequest
{
BucketName = bucketName,
Key = fileKey,
Expires = DateTime.UtcNow.AddSeconds(expirationTimeInSeconds),
Verb = HttpVerb.GET,
ResponseHeaderOverrides = new ResponseHeaderOverrides
{
ContentDisposition = $"attachment; filename = \"test.json\""
}
};
string url = client.GetPreSignedURL(request);
Then in the front end I simply do
window.location.href = url
Upvotes: 0
Reputation: 103
Others have mentioned simulating a click within the DOM or React, but another option is to use window.open(). You can set the target attribute to _blank
to open a tab, but you do need window.open()
inside the click event to prevent popup blockers from stopping the functionality. There's some good discussion on the subject here. I found this to be a better solution than simulating a click event.
Here's an example (though there may be more needed depending on how you fetch the signed_url).
function downloadDocument() {
const signedurlPromise = fetch("/signed_url")
signedurlPromise.then((response) => {
window.open(response.signed_url, "_blank");
})
}
Upvotes: 1
Reputation: 6360
The way I did it was different and has the advantage of being able to see the progress of the download as the file is being downloaded. If you're downloading a large file then it makes a difference UX wise as you see feedback immediately.
What I did was:
content-disposition
to `attachment<a url='https://presigned-url' download>Download me</a>
Upvotes: 1
Reputation: 74
The other answer does direct DOM manipulation, creates a blob, which looks as though it buffers the whole file in memory before sending it to the user and also creates a new link each time you download. A react-y of doing is:
const downloadFileRef = useRef<HTMLAnchorElement | null>(null);
const [downloadFileUrl, setDownloadFileUrl] = useState<string>();
const [downloadFileName, setDownloadFileName] = useState<string>();
const onLinkClick = (filename: string) => {
axios.get("/presigned-url")
.then((response: { url: string }) => {
setDownloadFileUrl(response.url);
setDownloadFileName(filename);
downloadFileRef.current?.click();
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<a onClick={() => onLinkClick("document.pdf")} aria-label="Download link">
Download
</a>
<a
href={downloadFileUrl}
download={downloadFileName}
className="hidden"
ref={downloadFileRef}
/>
</>)
See here for more info https://levelup.gitconnected.com/react-custom-hook-typescript-to-download-a-file-through-api-b766046db18a
Upvotes: 2
Reputation: 151
I resolved my problem with a redux action. With one click I call my action, who return my pre-signed URL, then automatically click the link. This trigger download event with the original file name when I upload it to S3.
export const downDoc = (docId) => async dispatch => {
const res = await axios({ url: 'myApiCall', method: 'GET', responseType: 'blob' })
.then((response) => {
console.log(response)
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `${docId.originalName}`);
document.body.appendChild(link);
link.click();
});
Upvotes: 6