Zod validator / validate image

Viewed 362

I have a problem, I need to validate an image with zod. I am searching for 3 hours. I can't find out on validating the image? Can anyone help me out to fix this? zod must have image validate yes?

const payloadSchema = z.object({
    image: z.record(z.string()),
})

Find something like this, but how can I add the image that is 3 mb max and it's type must be "jpg" "png" or "gif"

2 Answers

I would go about this by adding a refinement to a zod schema for File. The superRefine helper can be used to attach new issues to an existing schema as follow up validations.

import { z } from 'zod';

const MB_BYTES = 1000000; // Number of bytes in a megabyte.

// This is the list of mime types you will accept with the schema
const ACCEPTED_MIME_TYPES = ["image/gif", "image/jpeg", "image/png"];

// This is a file validation with a few extra checks in the `superRefine`.
// The `refine` method could also be used, but `superRefine` offers better
// control over when the errors are added and can include specific information
// about the value being parsed.
const imageSchema = z.instanceof(File).superRefine((f, ctx) => {
  // First, add an issue if the mime type is wrong.
  if (!ACCEPTED_MIME_TYPES.includes(f.type)) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: `File must be one of [${ACCEPTED_MIME_TYPES.join(
        ", "
      )}] but was ${f.type}`
    });
  }
  // Next add an issue if the file size is too large.
  if (f.size > 3 * MB_BYTES) {
    ctx.addIssue({
      code: z.ZodIssueCode.too_big,
      type: "array",
      message: `The file must not be larger than ${3 * MB_BYTES} bytes: ${
        f.size
      }`,
      maximum: 3 * MB_BYTES,
      inclusive: true
    });
  }
});

This should validate with the parameters you defined, but assumes you have a handle on the File that you are validating. If you are getting files from an <input type="file" /> element, you could potentially sidestep needing to validate the MIME type by adding an accept attribute to your input.

Try this it, it seems simple and it works for me:

const MAX_FILE_SIZE = 500000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp"];

const someSchema = z.object({
  image: z
    .any()
    .refine((file) => file?.size <= MAX_FILE_SIZE, `Max image size is 5MB.`)
    .refine(
      (file) => ACCEPTED_IMAGE_TYPES.includes(file?.type),
      "Only .jpg, .jpeg, .png and .webp formats are supported."
    )
})

And then the error should be displayed with:

formState.errors?.image?.message

One thing to note though is what kind of object are you getting from your input. Check if its a File object or a File[] array. I am using it with react-dropzone so I configured it to save a single File object. If it were an array you would have to change the schema to this:

const MAX_FILE_SIZE = 500000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp"];

const someSchema = z.object({
  image: z
    .any()
    .refine((files) => files?.[0]?.size <= MAX_FILE_SIZE, `Max image size is 5MB.`)
    .refine(
      (files) => ACCEPTED_IMAGE_TYPES.includes(files?.[0]?.type),
      "Only .jpg, .jpeg, .png and .webp formats are supported."
    )
})
Related