How to fix ReferenceError: Blob is not defined issue when using heic2any library in nextjs?

1.2k Views Asked by At

I am facing a problem where I have implemented everything and its working fine actually, but the main problem is that whenever I try to reload the page it will give me a error saying: ReferenceError: Blob is not defined I am using heic2any library in nextJs I am doing that whenever the user selects a heic file onChange event, it will convert the heic file to png. everything is working properly but if I reload the page gets crash giving the error ReferenceError: Blob is not defined here is the function where I am using heic2any library. If I comment it out, it will work fine, but then I can't convert heic file to any.

const onFilesChange = (event: ChangeEvent<HTMLInputElement>): void => {
      const { files: assets } = event.target;
      if (!assets?.length) {
        return;
      }
       
      
      const validFiles = Array.from(assets).filter(
        ({ size }) => size < MAX_FILE_SIZE + 1
      );
      console.log("FILE SIZE of the file uplaoded::", validFiles.length);
  
      if (validFiles.length < assets.length) {
        // TODO: show error
        alert("File size exceeds,file size should not be more than 10MB");
        setFiles([]);
        return;
      }
      //Todo: .jpg, .jpeg, .png, .heic, .pdf, .doc, .docx
      const fileFormat = Array.from(assets).filter(
        ({type}) => type === "image/jpg" || type === "image/png" || type === "image/jpeg" || type      === "application/pdf" || type === "image/heic" || type === "application/msword" || type ===    "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
      )
      let heic = assets[0];
      if(heic.name.endsWith('.heic')){
      let randomName = Math.floor(Math.random() * 184) + 1;
      heic2any({
        blob: heic,
        toType: "image/png",
        quality: 0.7,
      }).then((blob) => {
        // let newFile = new File(blob,`heic-${randomName}.jpg`,{type:'image/jpeg'})
        let heicFile = new File([blob], `heicImage-${randomName}.png`, {
          type: "image/png",
        });
        setFiles([heicFile])
      });
        }
     
      if(fileFormat.length < assets.length){
        alert("Invalid file format,only jpg,png,jpeg,heic,pdf,doc & docx file format required");
        setFiles([]);
        return;
      }
  
      setFiles([...files, ...validFiles]);
    };

I tried multiple things but non of them worked, therefore I am looking for a solution here.

Thank you.

1

There are 1 best solutions below

1
On

TLDR: To fix the error, use a dynamic import to load the heic2any module on the client-side (code sample present at the bottom of this answer)

I was struggling with the same issue today but I was able to fix it with some ChatGPT assistance! I sent the error message to ChatGPT and it explained the following:

The ReferenceError: Blob is not defined error typically occurs when the Blob object is not recognized by the JavaScript runtime environment. This can happen in environments where the Blob object is not natively supported, such as in Node.js or certain versions of Internet Explorer.

I thought "well that's weird, I'm doing front-end development, why is it talking about Node.js?" I don't have deep NextJS knowledge, but in searching about this I learned that NextJS runs both server and client, and some files will run on one or the other. I asked ChatGPT "why is my nextjs code being run server-side?" and it replied:

In Next.js, the code in your pages directory is run on both the server and the client. This is because Next.js uses server-side rendering (SSR) by default, which means that the initial page rendering is done on the server and then sent to the client as HTML.

The problem happens when you import the heic2any package, so I asked ChatGPT "can I make it so that an import only happens on the client side?" and it replied:

Yes, you can use dynamic imports to load a module only on the client-side. Dynamic imports are a feature of ES6 that allow you to load a module at runtime instead of at compile time.

It also provided a code example. The first example didn't work out of the box, but with a little more questioning, I arrived at the solution. This is what worked for me (using Typescript):

import { useState } from 'react';

async function convertHeicToPng(imageToConvert: File) {
  // This module will only be loaded on the client side
  const heic2any = (await import("heic2any")).default;

  const convertedBlob = await heic2any({
    blob: imageToConvert,
    toType: "image/png",
    quality: 0.7,
  });

  const fileExt = path.extname(imageToConvert.name);
  const fileNameNoExt = path.basename(imageToConvert.name, fileExt);

  const convertedFile = new File(
    [convertedBlob as Blob],
    `${fileNameNoExt}.png`,
    {
      type: "image/png",
    }
  );

  return convertedFile;
}

function MyComponent() {
   ...
   const handleImageUpload = async (
      e: React.ChangeEvent<HTMLInputElement>
   ): Promise<void> => {

    const filesToUpload = e.target.files as FileList;

    for (let i = 0; i < filesToUpload.length; i++) {
      let file = filesToUpload[i];
      if (file.type === "image/heic") {
        file = await convertHeicToPng(file);
      }
      // UPLOAD IMAGE
    }
  };