Skip to content

Instantly share code, notes, and snippets.

@issam-seghir
Last active May 2, 2024 14:50
Show Gist options
  • Save issam-seghir/81ae88a73a5715ef66f69ffab6a0401c to your computer and use it in GitHub Desktop.
Save issam-seghir/81ae88a73a5715ef66f69ffab6a0401c to your computer and use it in GitHub Desktop.
validate a file input with zod
const MAX_FILE_SIZE = 500000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp"];
const RegistrationSchema = z.object({
profileImage: 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."
),
});
// best alternative to.any() for files
z.custom<File>((v) => v instanceof File)
// or
z.instanceof(File),
const ACCEPTED_FILE_TYPES = ['application/json']
z.custom<File>(val => val instanceof File, 'Please upload a file')
.refine(
file => ACCEPTED_FILE_TYPES.includes(file.type),
{ message: 'Please choose .json format files only' }
),
// --------------- Validate arry of files
const formSchema= z.object({
image: z
.array(z.custom<File>())
.refine(
(files) => {
// Check if all items in the array are instances of the File object
return files.every((file) => file instanceof File);
},
{
// If the refinement fails, throw an error with this message
message: 'Expected a file',
},
)
.refine(
(files) => files.every((file) => file.size <= MAX_FILE_SIZE),
`File size should be less than 2mb.`,
)
.refine(
(files) => files.every((file) => ACCEPTED_IMAGE_TYPES.includes(file.type)),
'Only these types are allowed .jpg, .jpeg, .png and .webp',
),
})
// -------------- another approche -------------------------------
z.custom<FileList>().superRefine((files, ctx) => {
if (files.length === 0) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'File must be provided',
})
return false
}
if (
!['image/webp', 'image/png', 'image/svg', 'image/jpg', 'image/jpeg'].includes(
files[0].type
)
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'File must be a valid image type',
})
return false
}
if (files[0].size > 1024 * 1024 * 5) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'File must be less than 5MB',
})
return false
}
return true
}),
// another approche
const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpg", "image/jpeg"];
const MAX_IMAGE_SIZE = 4; //In MegaBytes
const sizeInMB = (sizeInBytes: number, decimalsNum = 2) => {
const result = sizeInBytes / (1024 * 1024);
return +result.toFixed(decimalsNum);
};
const UserGeneralInfoSchema = z.object({
profileImage: z
.custom<FileList>()
.refine((files) => {
return Array.from(files ?? []).length !== 0;
}, "Image is required")
.refine((files) => {
return Array.from(files ?? []).every(
(file) => sizeInMB(file.size) <= MAX_IMAGE_SIZE
);
}, `The maximum image size is ${MAX_IMAGE_SIZE}MB`)
.refine((files) => {
return Array.from(files ?? []).every((file) =>
ACCEPTED_IMAGE_TYPES.includes(file.type)
);
}, "File type is not supported"),
});
// for server is FILE doesnt exist :
const formData = await request.formData()
const alt = z.string().min(3).parse(formData.get('alt'))
const file = z.instanceof(Blob).parse(formData.get('file')) as File
console.log(file.name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment