Zod File Upload validation

I'm currently making a file upload form is there any way to validate a file in zod? I tried this https://github.com/colinhacks/zod/issues/387#issuecomment-1191390673 but it didn't seem to work No matter what file i upload it just says image is required https://rentry.co/ndimv A gist of my code
// Imports go here
const MAX_FILE_SIZE = 500000 const ACCEPTED_IMAGE_TYPES = [ "image/jpeg", "image/jpg", "image/png", "image/webp", ] const formSchema = z.object({ course_id: z.string().min(1, { message: "Course name is required" }), year: z.string().m...
GitHub
Validating file input · Issue #387 · colinhacks/zod
Coming from Yup. I'm trying to validate a required file input with no success: file: z.any().refine(val => val.length > 0, "File is required") Any tips?
43 Replies
fosslover
fossloverOP2y ago
that error message is defined in the formSchema isn't that enough?
Keef
Keef2y ago
When I was doing this I had to do it in a slightly different way The api for attaching files was a file list actually so I had to refine the inputs I’ll share what works for me in a second when I’m at my pc If I forget plz @ me
fosslover
fossloverOP2y ago
guess you forgot could you please share that?
Keef
Keef2y ago
export const assetForm = z.object({
file: z
.custom<FileList>()
.refine((fileList) => fileList.length === 1, 'Expected file')
.transform((file) => file[0] as File)
.refine((file) => {
return file.size <= maxFileSize;
}, `File size should be less than 1gb.`)
.refine(
(file) => ACCEPTED_IMAGE_TYPES.includes(file.type),
'Only these types are allowed .jpg, .jpeg, .png, .webp and mp4',
),
});
export const assetForm = z.object({
file: z
.custom<FileList>()
.refine((fileList) => fileList.length === 1, 'Expected file')
.transform((file) => file[0] as File)
.refine((file) => {
return file.size <= maxFileSize;
}, `File size should be less than 1gb.`)
.refine(
(file) => ACCEPTED_IMAGE_TYPES.includes(file.type),
'Only these types are allowed .jpg, .jpeg, .png, .webp and mp4',
),
});
Just using an Input with type file to prompt the user works well 😄
fosslover
fossloverOP2y ago
Unhandled Runtime Error
TypeError: fileList is undefined
Unhandled Runtime Error
TypeError: fileList is undefined
Am I missing something?
Keef
Keef2y ago
thinkies version zod, next, react are you running what browser as well (but I HOPE ITS NOT THIS) otherwsie I'm gonna be fixing this rn
fosslover
fossloverOP2y ago
chrome
Keef
Keef2y ago
The comment you linked in OP is basically the same way I do it aside from using z.custom instead of z.any. Did you try this one? https://github.com/colinhacks/zod/issues/387#issuecomment-1535668926
GitHub
Validating file input · Issue #387 · colinhacks/zod
Coming from Yup. I'm trying to validate a required file input with no success: file: z.any().refine(val => val.length > 0, "File is required") Any tips?
fosslover
fossloverOP2y ago
I don't know I just took the project from the shelf (basically I did touched it for a month or so) maybe its older lemme check zod - v3.21.4 react - 18.2.0 next - 13.4.4
Keef
Keef2y ago
This wasnt fun when I was implementing it. Weird to see it happen to you to but my solution not working thinkies . The solution I shared above didn't work for me so I was hoping it would for you All the "thumbs-up" reactions on the commetn and it isn't actually working 😭 versions look fine What is your handle upload doing? Thats probably it The form state doesn't have a value set so thats why its erroring even tho locally the input might actually have something. check the state using react dev tools to see if the input has anything
fosslover
fossloverOP2y ago
const handleUpload = (e: any) => { setFiles(e.target.files) } thats it
Keef
Keef2y ago
I'm assuming you want to let the use upload a file and submit. I do the same thing but I leave the upload in the handle submit portion and the form state holds my file
fosslover
fossloverOP2y ago
it just sets the state
Keef
Keef2y ago
Well thats why Your formstate doesn't have it Thats why its erroring console.log(form) and you'll be able to see it you might have to dig thru the properties or just set up a
const file = watch("file")
console.log(file)
const file = watch("file")
console.log(file)
Then try uploading and see what happens
fosslover
fossloverOP2y ago
iirc it logged the file path i did this long ago
Keef
Keef2y ago
Just get rid of it the onChange handler Its not necessary You are duplicating your state since the form already holds its own batch of state. It is surprisingly getting the value so thats good but lets clean it up to try to make it easier to debug
fosslover
fossloverOP2y ago
lemme see what I do with that files thing
Keef
Keef2y ago
const submit = async (data: AssetForm) => {
const uploadLocation = await uploadURL.mutateAsync({
id: props.profileId,
name: data.name,
fileType: data.file.type,
});

const uploadResult = await upload.mutateAsync([uploadLocation.url, data.file]);
}
const submit = async (data: AssetForm) => {
const uploadLocation = await uploadURL.mutateAsync({
id: props.profileId,
name: data.name,
fileType: data.file.type,
});

const uploadResult = await upload.mutateAsync([uploadLocation.url, data.file]);
}
Its as easy as this I do s3 puts here the upload mutation is clientside but its just a put request You should share a gist with your submit function, anything that messes with the files
fosslover
fossloverOP2y ago
now I get why I used onChange handler basically on submit i just get C:\fakepath\filename.ext from the form so I use the onChange handler to get the actual file
Keef
Keef2y ago
the browser should be able to do that for you but you really need to provide what i asked for if you want better help 🙂
fosslover
fossloverOP2y ago
yeah lemme do it
const onSubmit = async (data: any) => {
let formData = data
// Replacing the file object with the file name
formData["file"] = files![0]
await UploadFile(formData)
callToast()
form.reset()
}
const onSubmit = async (data: any) => {
let formData = data
// Replacing the file object with the file name
formData["file"] = files![0]
await UploadFile(formData)
callToast()
form.reset()
}
this is what my onSubmit does that files is what updated by the onChange handler
Keef
Keef2y ago
So what are you trying to do here Just upload users file?
fosslover
fossloverOP2y ago
yup
Keef
Keef2y ago
The name for the file is on the attached file object with the form data. That line is probably the cause of all your issues formData["file"] = files![0] this line whats upload file do?
fosslover
fossloverOP2y ago
gets the form data inserts a db entry and also uploads the file to supabase storage bucket so if I fail to do that I get C:\fakepath\filename.ext this string in formData["file"] not the file
Keef
Keef2y ago
can you share upload file
fosslover
fossloverOP2y ago
yeah
fosslover
fossloverOP2y ago
https://rentry.co/bpxeb (Spaghetti warning)
import { get } from "http"
import { getUser, supabase } from "@/config/auth" export default async function UploadFile(form: any) { let courseName = await getCourseName(form.course_id) let subjectName = await getSubjectName(form.subject_id) let subjectCode = await getSubjectCode(form.subject_id) const fil...
Keef
Keef2y ago
looks fine your issue is your zod schema is expect x but you aren't providing that. After getting rid of the onchange does it submit?
Keef
Keef2y ago
Heres whats a successful data form submissions looks like:
Keef
Keef2y ago
We should already have a handle to the file so its just about dropping it where we need it. If the zod error is gone and it submits but fails in the operation its gonna be that line. But even before the submission function step theres some decoupling in your state change your form field to be z.any().refine(x => {console.log(x) // see what type it is}) To see if you have the right input to begin with Then start adding the refinements
fosslover
fossloverOP2y ago
are you getting only the file in the form?
fosslover
fossloverOP2y ago
all I get is this on the console.log(x)
Keef
Keef2y ago
I'm using this form But even with z.any its the same type. The underlying type (original input) doesn't change no matter what we say with our form It might be the controlled input but yeah this is what I was dealing with a few weeks sip
fosslover
fossloverOP2y ago
that gave me different errors like this so what should I do then?
Keef
Keef2y ago
Try uncontrolled input. Thats what I'm using
Keef
Keef2y ago
GitHub
Validating file input · Issue #387 · colinhacks/zod
Coming from Yup. I'm trying to validate a required file input with no success: file: z.any().refine(val => val.length > 0, "File is required") Any tips?
Keef
Keef2y ago
If you get "image is required" that means your form data is not actually correct when you try to submit so you need to take a look at the form state
fosslover
fossloverOP2y ago
stuck with image is required no matter what I upload its because what I get from the form is a string not a file
Keef
Keef2y ago
Yeah it might be the controlled input Try without it
Keef
Keef2y ago
Brendonovich
The Ultimate Form Abstraction
Typesafe forms in React? Sounds like a job for React Hook Form and Zod!
fosslover
fossloverOP2y ago
I'll try that {...register('name')} thing right?
Keef
Keef2y ago
interface Props extends ComponentProps<'input'> {
name: string;
label: string
}

const Input = forwardRef<HTMLInputElement, Props>((props, ref) => {
const form = useFormContext();
const state = form.getFieldState(props.name, ctx.formState);

return (
<div>
<label htmlFor={props.name}>{props.label}</label>
<input {...props} id={props.name} ref={ref} />
{state.error && <p>{state.error.message}</p>}
</div>
);
});
interface Props extends ComponentProps<'input'> {
name: string;
label: string
}

const Input = forwardRef<HTMLInputElement, Props>((props, ref) => {
const form = useFormContext();
const state = form.getFieldState(props.name, ctx.formState);

return (
<div>
<label htmlFor={props.name}>{props.label}</label>
<input {...props} id={props.name} ref={ref} />
{state.error && <p>{state.error.message}</p>}
</div>
);
});
This one It works very well 🙂 s/o to brendon
Want results from more Discord servers?
Add your server