restrict image dimension in react-dropzone

I am using react-dropzone for image upload. Everything is working fine. Validation for image size is also working fine. But I could not check the dimension for image. I want to validate the image’s width and height to enforce user to upload the image in between those specified width and height. I tried image.addEventListener('load') but this is not working.

Here is what I have done

export const UploadField = ({ preview, label, uploadProps, ...props }) => {
  const {
    input: { onChange },
    disabled
  } = props;

  const {
    isDragActive,
    getRootProps,
    getInputProps,
    isDragReject,
    rejectedFiles
  } = useDropzone({
    onDrop: files => {
      onChange(
        files.map(file => {
          const image = new Image();
          image.addEventListener("load", () => {
            console.log("image", image);
          });
          return Object.assign(file, {
            preview: URL.createObjectURL(file)
          });
        })
      );
    },
    ...uploadProps
  });

  const isFileTooLarge =
    rejectedFiles.length > 0 && rejectedFiles[0].size > uploadProps.maxSize;

  const files = props.input.value;

  if (disabled) {
    return null;
  }

  return (
    <>
      {label && <Label>{label}</Label>}
      <DropzoneContainer {...getRootProps()}>
        <input {...getInputProps()} />
        {!isDragActive && "Click here or drop a file to upload!"}
        {isDragActive && !isDragReject && "Drop it like it's hot!"}
        {isDragReject && "File type not accepted, sorry!"}
        {isFileTooLarge && (
          <div className="text-danger mt-2">File is too large.</div>
        )}
      </DropzoneContainer>
      <div>
        {files && files !== undefined ? (
          <>
            <Preview files={files} isLocal />
          </>
        ) : (
          <Preview files={preview} isLocal={false} />
        )}
      </div>
    </>
  );
};

export default UploadField;

UploadField.defaultProps = {
  uploadProps: {
    accept: "image/*",
    multiple: false,
    minSize: 0,
    maxSize: 5242880
  }
};

const DropzoneContainer = styled.div`
  width: 100%;
  padding: 14px;
  border-width: 2px;
  border-radius: 2px;
  border-color: ${props => getColor(props)};
  border-style: dashed;
  background-color: #fafafa;
  color: #bdbdbd;
  outline: none;
  transition: border 0.24s ease-in-out;
`;

const getColor = props => {
  if (props.isDragAccept) {
    return "#00e676";
  }
  if (props.isDragReject) {
    return "#ff1744";
  }
  if (props.isDragActive) {
    return "#2196f3";
  }
  return "#eeeeee";
};

2 thoughts on “restrict image dimension in react-dropzone”

  1. You never set the src for the image so your event handler never fires. Try setting image.src = URL.createObjectURL(file). Once the file loads, your ‘load’ handler will fire.

    Try changing the contents of your onDrop callback to include this:

    const filteredImages = [];
    let counter = 0;
    
    files.map(file => {
        const image = new Image();
        image.addEventListener('load', () => {
            console.log(`${image.width}x${image.height}`)
    
            // only select images within width/height limits
            if (image.width < WIDTH_LIM & image.height < HEIGHT_LIM) {
                filteredImages.push(image)
            }
    
            // increment counter for each image we go through
            counter += 1;
    
            // if we have gone through all the files, handle the ones that
            // made it through the filter using `handleImages` function
            if (counter === files.length) handleImages(filteredImages);
        });
        image.src = URL.createObjectURL(file)
    })
    
    Reply
  2. If you want to validate dimensions before onDrop, you can use getFilesFromEvent and validator callbacks like below.

    Pros
    As with errors such as maxSize and accept, you can get files that are stuck in validation for dimention from rejectedFiles.

    Cons
    f you are using typescript, you have to eliminate the type error with any type.

    const {
        isDragActive,
        getRootProps,
        getInputProps,
        isDragReject,
        rejectedFiles
      } = useDropzone({
        getFilesFromEvent: async (event) => {
          const files = event.target.files
          const promises = []
          for (let index = 0; index < files.length; index++) {
            const file = files[index]
            const promise = new Promise((resolve, reject) => {
              const image = new Image()
              let url: string
              image.onload = function () {
                file.width = image.width
                file.height = image.height
                resolve(file)
              }
              url = URL.createObjectURL(file)
              image.src = url
            })
            promises.push(promise)
          }
          return await Promise.all(promises)
        },
        validator: (file) => {
          // You can access width/height properties
          if(file.width < MIN_WIDTH) {
            return {
              code: "small-width",
              message: `Image width must be greater than ${MIN_WIDTH}`,
            }
          }
          return null
        }
    
      });
    
    
    
    
    Reply

Leave a Comment