-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Validating file input #387
Comments
i guess that you can check if is a instace of File |
@wassim is this a File instance? If so just use |
@colinhacks thanks for your answer. I am also looking for a solution. Can i also check if the File MIME-Type is for e.g. an and when i try to use: file: z.instanceof(File) i get the error:
|
You really shouldn't use toZod, it's very fragile and not supported.. It's also totally unnecessary here because you're not trying to define recursive schemas (which is the only scenario where it sorta comes in handy). Just get rid of the |
@niklasgrewe If you're writing code that will run in the browser, you need to tell TypeScript to include browser-specific classes like File. Add "dom" to your "lib" array in tsconfig.json:
|
Hey this still is not fixing the |
I am getting the same error as @AlvaroAquijeDiaz . "dom" is already in the config and it's not working. FYI: I am currently writing a schema using nextjs and react-hook-forms.
|
Any ideas? |
I am experiencing the same issue. |
@AlvaroAquijeDiaz @niklasgrewe @colinhacks @d4vvv So I was refactoring and doing some code cleanup on my forms component and I think I accidentally found a temporary solution. I say temporary because it doesn't completely address the problem. I added When I imported it into my forms component, the See if that works for you. |
@CoreyGumbs I'll definitely try this, thanks |
@CoreyGumbs Tried your solution but no luck on my end. Moved the schema into its own file like you said but got the same error. @AlvaroAquijeDiaz Did it work for you? |
@HARAJLI98 yep still getting errors, I think we'll just have to use the |
@HARAJLI98 @AlvaroAquijeDiaz Sorry for the late reply, just getting back to my project. So I don't know what I did, or what happened, and wasn't able to reproduce the result, but I too am getting an error when trying this on a new form Im making. I created an object with the properties from the FILE/FILELIST obj just so I could move forward and test my upload on my form:
this is probably not the best solution but it did get rid of the type of errors I was getting, even with .any() |
Putting this here if anyone still has this problem. The solution that worked for me was this one: 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."
),
}); |
Hey I think this will solve your problem, I've tried it myself and it works |
https://gist.github.com/bryantobing12/443f2c417b4ed537284be7cf55bf10ad, this is what works for me The previous link is broken since I changed my username. |
I resolved this in my scenario |
@officialtbabs Could you please share with us the code where you integrated the form with the schemas defined above ? |
This solution worked for me! |
To make it more type safe. I do z.any() as ZodType<File> Hope you find it useful as well. UPDATE!! z.custom<File>() |
Just saved me so much time!
|
This will bypass typechecking. To make it typesafe, provide a validation function like this: z.custom<File>((v) => v instanceof File) |
This is how i managed to do it with an array 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',
),
}) |
Hey everyone, you can use const schema = z.object({
file: z.instanceof(File),
}) It will infer the type and ensure that it is instance of |
@PratikDev i have tried edited demo. now its stuck on this refine 😕 refine((file) => file?.type === 'application/pdf', 'Must be a PDF.') |
@Kavindu-Wijesekara it's because |
@PratikDev Oh i didn't see that. Anyway thank you very much. Now it's working perfectly 👌 |
Reason and SolutionHey everyone. I just found it. Using EditI tried using |
Hi
Did you get |
i'm not sure if |
This works, thanks! |
From looking into this issue, I see that there's no implementation of the For TypeScript If you enable the Example for Next.js users: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) |
This is how to handle single or multiple files. 👌 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 me import File from "buffer" helped, here is my snippet: import { File } from "buffer";
const fileSchema = z.instanceof(File, { message: "Required" });
const imageSchema = fileSchema.refine(
(file) => file.size === 0 || file.type.startsWith("image/")
);
const addScheme = z.object({
name: z.string().min(1),
file: fileSchema.refine(
(file) => file.size > 0,
"File size was 0, please upload a proper file!"
),
image: imageSchema.refine(
(file) => file.size > 0,
"File size was 0, please upload a proper file!"
),
}); |
This worked for me, thanks. |
some quick maths i use 2 ** 18 = 256 MB |
Thanks to the other responses in this thread. This worked for me to collect multiple files. const MAX_FILE_SIZE = 5000000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/webp"];
const schema = zod.object({
image: zod
.instanceof(FileList, { message: "Required" })
.refine((file) => file?.length > 0, 'A file is required.')
.refine((files) => {
for (let i = 0; i < files.length; i++) {
console.log(i, files[i])
if (files[i].type in ACCEPTED_IMAGE_TYPES) {
if (!ACCEPTED_IMAGE_TYPES.includes(files[i].type)) return false; // Check if it's an accepted image type
}
}
return true;
}, 'Must be a valid image.')
.refine((files) => {
for (let i = 0; i < files.length; i++) {
if (files[i].size > MAX_FILE_SIZE) return false; // Check if size exceeds max size
}
return true;
}, 'Max size reached.')
});
.......
<Controller
control={control}
name="image"
render={({ field: { value, onChange, ...field } }) => (
<FormControl error={Boolean(errors.image)} fullWidth>
<InputLabel required>Superpower card image</InputLabel>
<input
type="file"
accept={ACCEPTED_IMAGE_TYPES.join(', ')}
multiple
onChange={(event) => onChange(event?.target?.files ?? "")}
{...field}
/>
{errors?.image ? <FormHelperText>{errors.image.message}</FormHelperText> : null}
</FormControl>
)} |
It is so complicated to only validate multiple files, is there a way to simplify this? |
Becareful of using |
export const PostUploadFileSchema = z
.object({
"image[]": z
.custom<Blob>()
.refine(
(blob) => {
return blob.size <= MAX_FILE_SIZE;
},
{
message: `More than ${MAX_FILE_SIZE} are not allowed`,
}
)
.refine((blob) => ACCEPTED_IMAGE_TYPES.includes(blob?.type), {
message: "Only .jpg, .jpeg, .png and .webp formats are supported.",
}),
})
.openapi("Post Upload File");
// To convert Blob to File
for (let image of images) {
image = new File([image], image.name, {
type: image.type,
});
if (image instanceof File) {
await uploadFile(image);
}
} |
didn't know that "any" could solve my problem, thank you 👍 |
`const MAX_FILE_SIZE = 510241024 ` |
If image is optional: avatar: z.custom<File | undefined>()
.refine(file => (!file || file?.size <= MAX_FILE_SIZE)!,
{message: "Max image size is 5MB."})
.refine(file => (!file || ACCEPTED_IMAGE_TYPES.includes(file?.type))!,
".jpg, .jpeg, .png, .webp, files are accepted only"), |
When i do that, i have this error : |
If anyone is working with shadcn-ui form components and is trying to validate the schema via zod, you can refer the following article. TLDR; if you are working on client components in next.js change your schema to const formSchema = z.object({
file: z.instanceof(FileList).optional(),
}); When defining the form const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
});
const fileRef = form.register("file"); Then later when rendering the component <Input type="file" placeholder="shadcn" {...fileRef} /> |
so want to be optional ?
if want to limit the size 510241024 is 5mb if you don't want to limit the size remove that one
###so you want optional
it will work |
here's an alternative to handle single or multiple files on both browser and server: const Schema= z.object({
files: z
.preprocess(
(value) => (Array.isArray(value) ? value : [value]),
z.array(z.instanceof(File))
)
// ...
}); |
This gives me the following error when I deploy on Vercel.
|
Fixed it with this from the originally referenced issue. file: typeof window === 'undefined' ? z.any() : z.instanceof(FileList) |
Coming from Yup. I'm trying to validate a required file input with no success:
Any tips?
The text was updated successfully, but these errors were encountered: