Makuhari Development Corporation
2 min read, 366 words, last updated: 2022/8/1
TwitterLinkedInFacebookEmail

Canvas Image CORS Issue

The required feature is to load an S3 image to edit in the browser, then upload the edited one.

The edit operation could be adding a mask for part of the image.

We used konva, react-konva to achieve it.

The Problem

First, we need to load the original image as a DOM element into Canvas, we used use-image

import useImage from 'use-image';
 
const [image] = useImage(imageUrl);

We instantly faced the CORS issue, after the ops updated the header options for the AWS S3 bucket, however, the problem existed.

We thought adding the crossorigin props anonymous could help but with no luck.

const [image] = useImage(imageUrl, 'anonymous');

After some tests, we found that the CORS error won't appear only if it's the first time seeing that image, this quickly leads us to think about the problem of cache.

The browser would auto-cache the resources including the images of couse, but if we read the image as a DOM element to use in Canvas, later on, the second request that went to the cache would cause an unexpected CORS error!

ref: https://stackoverflow.com/a/37455118/11872246

The Solution

All the solutions lead us to think about not using the cached image.

To achieve this, we quickly came to the thought that we can simply request it once more when we are using the image as a DOM element.

export default function ImageEditor({
  imageUrl,
  ...props
}: ImageEditorProps) {
 
  const [acquiredImageUrl, setAcquiredImageUrl] = useState<string>('');
  const [image] = useImage(acquiredImageUrl, 'anonymous');
 
  useEffect(() => {
    const acquireUsedImage = async () => {
      const res = await request.getImage(imageUrl);
      const url = URL.createObjectURL(res.data);
      setAcquiredImageUrl(url);
    };
    !acquiredImageUrl && imageUrl && acquireUsedImage();
  }, [acquiredImageUrl, imageUrl]);
 
  ...
  return (
    ...
  )
}

Still it won't work, finally, we noticed that we forgot to add the no-cache header for that new request.

  getImage<T = any>(url: string): AxiosPromise<T> {
    return service({
      method: 'get',
      url: `${window.location.protocol}//${url?.split('://')?.[1]}`,
      headers: { 'Cache-Control': 'no-cache' },
      responseType: 'blob',
    });
  },

Notice that the we modified the URL to keep the same protocol under http and https to prevent being blocked.

After that, the image could be loaded normally and that's the end of this story!

Makuhari Development Corporation
法人番号: 6040001134259
サイトマップ
ご利用にあたって
個人情報保護方針
個人情報取扱に関する同意事項
お問い合わせ
Copyright© Makuhari Development Corporation. All Rights Reserved.