Reputation: 999
I'm trying to download .jpg & .pdf images from S3 which has following settings
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Following the approach similar to - (How do I download a file with Angular2), everything works fine in Firefox. In chrome, PDF download works perfectly but when I try to download an image, always getting typical CORS Error for the GET request.
downloadFile(url, type, fileName) {
this.http.getFile(url, { responseType: 'blob' }).
subscribe(data => this.downloadFileComplete(data, type, fileName));
}
downloadFileComplete(data: any, type: string, fileName: string) {
let fileType = (type === 'IMAGE') ? 'image/jpeg' : 'application/pdf';
var blob = new Blob([data], { type: fileType.toString() });
if (type === 'IMAGE') {
saveAs(blob, fileName + ".jpg");
}
else {
saveAs(blob, fileName + ".pdf");
}
}
Access to XMLHttpRequest at 'https://xxxxxxxxxxxxxx.s3.ap-south-1.amazonaws.com/certifications/3c35754f-b3c4-42f2-953a-8af52b5ed19bf2bc5370-9481-492a-8174-dfcc63d5a9bd.jpg' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Upon some research, I found this work-around (https://zyst.io/how-to-fix-aws-s3-chrome-and-safari-cors-on-images) where they suggest to use HTTP URL for S3. When I tried this, images download started working again. Can someone explain to me
Why GET call to fetch image throws CORS errors for image and not for PDF in Chrome?
Is there a proper solution for this problem? My site runs on HTTPS, therefore an HTTP based solution is not good.
Upvotes: 8
Views: 4507
Reputation: 309
Finally found a real solution to this problem. You can serve your images through Cloudfront, this tutorial explains the necessary config.
After this, you can point your saveAs URL to Cloudfront, and voilá, your image will be downloaded without any CORS problem.
Upvotes: 0
Reputation: 309
I tried several solutions until I was able to develop the following workaround for this problem.
I wrote this lambda function to get S3 object and return it as base64
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
const imageConversor = async (event, context, callback) => {
const { imageUrl } = event.queryStringParameters;
const params = { Bucket: "your-bucket-name", Key: imageUrl };
const response = await s3.getObject(params).promise();
const fileContent = response.Body.toString("base64");
callback(null, {
statusCode: 200,
body: JSON.stringify({
image: fileContent,
}),
headers: {
"Access-Control-Allow-Origin": "*",
},
});
};
// this base64toBlob function was written by Jeremy at https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
const base64toBlob = function (b64Data, contentType, sliceSize) {
contentType = contentType || "";
sliceSize = sliceSize || 512;
var byteCharacters = window.atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, { type: contentType });
return blob;
};
const onDownloadClick = async (url) => {
const data = await axios.get("/imageConversor?imageUrl=" + url.replace("https://your-bucket-name.s3.amazonaws.com/", ""));
const blobImage = base64toBlob(url);
saveAs(blobImage || url, `fileName.png`);
}
Upvotes: 2