Load browser-created images

I am using <Dropzone /> for uploading image and want to display the image once the user inputs. However, this is not supported.

<Image source={URL.createObjectURL(file)} />

Safari: Not allowed to load local resource: blob
Firefox: Security Error: Content at https://shopify.com/123/account/pages/456 may not load data from blob:https://extensions.shopifycdn.com/567.

I believe this is CORS and can be tweaked?

Hey @devin, this is a known issue at the moment caused by the blob: url belonging to a different origin.

I think you have two options.

  1. Upload the image somewhere and use that URL as source for your Image.

  2. Convert the file content to a data url and pass this as source

function YourComponent() {
  const [image, setImage] = useState(null);
  <>
    <DropZone
      onInput={([file]) => {
        const reader = new FileReader();
        reader.onloadend = () => {
          const dataUrl = reader.result;
          setImage(dataUrl);
        };
        reader.readAsDataURL(file);
      }}
    ></DropZone>
    {image ? <Image source={image} /> : null}
  </>
}
1 Like

Thank you. For some reason I’m struggling to get the react state working with “multiple” input setup so I may have to go with single.

@devin do you mind sharing the code that you’re struggling with?

This code isn’t perfect but represents the effort. I’ll likely get back to it later this week because I need multiple to work well.

<DropZone multiple onInput={(files) => {
      files.forEach((file) => {
        const reader = new FileReader();
        reader.onloadend = () => {
          const dataUrl = reader.result as string;
          setFiles({ ...files, ...{ [file.name]: dataUrl } });
        };
        reader.readAsDataURL(file);
      })
    }} />
{files.map(file => <Image source={file.dataUrl} />)}

Alright @Robin-Shopify I got it working with this

onInput={async (files) => {
  
  const newFiles: (File & { dataUrl?: string })[] = [];

  await Promise.all(files.map(async (file) => {

    const dataUrl = await new Promise<string>((resolve) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve(reader.result as string);
      reader.readAsDataURL(file);
    });

    newFiles.push({
      ...file,
      dataUrl,
      // can't spread because these File object properties are not enumerable
      name: file.name,
      size: file.size,
      type: file.type,
      lastModified: file.lastModified
    });
  }));
1 Like