Reputation: 251
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
"
Upvotes: 15
Views: 45743
Reputation: 408
Try this out. It works for me. also pls check if you have a file array or a file object and make changes accordingly. i have also added file required validation also if needed
const MAX_FILE_SIZE = 3000000;
function checkFileType(file: File) { // file type checking
if (file?.name) {
const fileType = file.name.split(".").pop();
if (fileType && ["gif", "png", "jpg"].includes(fileType)) return true;
}
return false;
}
export const fileSchema = z.object({
z.any()
.refine((file: File) => file?.length !== 0, "File is required") // If you also wanna validate if the file exists
.refine((file) => file.size < MAX_FILE_SIZE, "Max size is 3MB.") // file size validation
.refine((file) => checkFileType(file), "Only .jpg, .gif, .png formats are supported."),`
});
Upvotes: 1
Reputation: 111
if you're facing the same issue, here is a workout that works for me:
image: z
.union([
z
.instanceof(File, { message: "Image is required" })
.refine(
(file) => !file || file.size !== 0 || file.size <= 5000000,
`Max image size is ${5000000}MB`
)
.refine(
(file) =>
!file ||
file.type === "" ||
["image/jpeg", "image/png", "image/jpg"].includes(file.type),
"Only .jpg, .jpeg, and .png formats are supported"
),
z.string().optional(), // Allow the existing image URL for editing mode
])
.refine((value) => value instanceof File || typeof value === "string", {
message: "Image is required",
}),
Upvotes: 1
Reputation: 41
In my case, I had to create an image upload with multiple files allowed and have to limit the file type to png, jpg, jpeg and webp
I faced the same issue, so after going through all the answers available here and also looking at the GitHub issue for the Image Validation. { In the issue, they had mentioned to use z.instanceof(File)
for node -v >=20.0.0
, but it didn't work for me even though I'm using v20.10.0
, so don't know about that }. This is my solution for the problem it works fine for all the cases I could think of.
const MAX_FILE_SIZE = 5000000
const ACCEPTED_IMAGE_TYPES = [
'image/jpeg',
'image/jpg',
'image/png',
'image/webp',
]
image: z
.any()
// To not allow empty files
.refine((files) => files?.length >= 1, { message: 'Image is required.' })
// To not allow files other than images
.refine((files) => ACCEPTED_IMAGE_TYPES.includes(files?.[0]?.type), {
message: '.jpg, .jpeg, .png and .webp files are accepted.',
})
// To not allow files larger than 5MB
.refine((files) => files?.[0]?.size <= MAX_FILE_SIZE, {
message: `Max file size is 5MB.`,
}),
Upvotes: 4
Reputation: 1160
I hade the same problem in Zod
image validation and I tried many ways to solve it. finally I got my solution as bellow code:
const MAX_FILE_SIZE = 500000; const ACCEPTED_IMAGE_TYPES = [ "image/jpeg", "image/jpg", "image/png", "image/webp", ];
image: z
.any()
.refine((files) => files?.length == 1, "Image is required.")
.refine(
(files) => files?.[0]?.size <= MAX_FILE_SIZE,
`Max file size is 5MB.`
)
.refine(
(files) => ACCEPTED_IMAGE_TYPES.includes(files?.[0]?.type),
".jpg, .jpeg, .png and .webp files are accepted."
),
Upvotes: 0
Reputation: 461
Try this it, it seems simple and it works for me:
const MAX_FILE_SIZE = 5000000;
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 = 5000000;
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."
)
})
Upvotes: 38
Reputation: 89
I encountered the same problem as you did and discovered a simpler way to solve it.
I'm also using Dropzone, but the concept is the same if you're using the File type, as long as it's not a vector file. Just don't use the "transform" and understand that the refinement will be for a single file.
avatar: z
.custom<FileList>()
.transform((file) => file.length > 0 && file.item(0))
.refine((file) => !file || (!!file && file.size <= 10 * 1024 * 1024), {
message: "The profile picture must be a maximum of 10MB.",
})
.refine((file) => !file || (!!file && file.type?.startsWith("image")), {
message: "Only images are allowed to be sent.",
}),
Upvotes: 6
Reputation: 9966
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.
Upvotes: 4