EDIT: What makes this different is that I’m perfectly able to access these resources with the browser using an <img>
tag, but can’t get the data with a fetch. The ‘similar’ questions don’t have this issue and/or don’t explain why a ‘get’ with the browser is different than a ‘get’ with a fetch request. Further, similar questions don’t have client-only solutions.
I have spent a day working on this.
I have a public s3 bucket with images. These images are accessible if you go to https://my-bucket-name.s3.amazonaws.com/path-to-my-image.jpg in your browser.
I have a reactjs application that shows these images using an <img src={imageFilename} />
jsx tag.
I want to add a button that will download the image to their computer for them.
The first method was to add a ‘download’ attribute to the a href
tag, like so:
const ImageDownloader = () => {
const imageUrl = 'https://my-bucket-name.s3.amazonaws.com/path-to-my-image.jpg';
const downloadImage = () => {
const link = document.createElement('a');
link.href = imageUrl;
link.setAttribute('download', 'downloaded-image.jpg'); // Specify the desired file name
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
return (
<div>
<img src={imageUrl} alt="Image to Download" style={{ width: '300px', height: 'auto' }} />
<br />
<button onClick={downloadImage}>Download Image</button>
</div>
);
};
This just opens a new tab to view the image and does not actually download the image. I don’t know why the ‘download’ attribute doesn’t download, but it appears not to be a viable tool.
Research indicates I might need to download the image with fetch or similar and use a blob. I would get CORS errors with the fetch (but, notably, I can easily view the images in the reactjs app) so I included ‘no-cors’ in the below; the browser console suggested this might be a solution. Turns out, it’s probably not.
const ImageDownloader = () => {
const imageUrl = 'https://my-bucket-name.s3.amazonaws.com/path-to-my-image.jpg';
try {
// Fetch the image with no-cors mode
const response = await fetch(imageUrl, {
mode: 'no-cors'
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'downloaded-image.jpg');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('Error downloading the image', error);
}
This gives me the ‘download’ in the browser, but the file is of zero bytes. If I look a the network response in the dev console, I see that the full file size got downloaded.
I tried npm packages file-saver
and js-file-download
too.
Long story short – I’ve got an image that I can easily access in my browser and I just want to let people download it. Please help.
3