CORS error while download image from S3 with HTTPS from chrome, but the same work in Firefox
Asked Answered
T

2

8

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

  1. Why GET call to fetch image throws CORS errors for image and not for PDF in Chrome?

  2. Is there a proper solution for this problem? My site runs on HTTPS, therefore an HTTP based solution is not good.

Trocki answered 21/5, 2020 at 8:37 Comment(1)
ok so now the work-around of making sure images are fetched via http also doesn't work anymore, apparently due to this: blog.chromium.org/2019/10/… so I am personally curious of any possible work around for this issue.Garlen
W
2

I tried several solutions until I was able to develop the following workaround for this problem.

Create a lambda function to serve s3 image as base64

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": "*",
    },
  });
};

Get base64 image than convert it to blob so you can download it

// this base64toBlob function was written by Jeremy at https://mcmap.net/q/42201/-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`);
}
Wedding answered 24/3, 2022 at 2:2 Comment(1)
Better solution explained bellowBeeson
W
0

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.

Wedding answered 17/7, 2023 at 14:55 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.