18

I have a form using reactjs + formik + yup. I have a multi file upload field. I want to validate the file format and max size using yup. How can I do this?

ComCool
  • 773
  • 4
  • 15
  • 28

6 Answers6

22

Expanding on Devin's answer, you can implement that validation with yup.

    const schema = Yup.object().shape({
       files: Yup.array()
         .nullable()
         .required('VALIDATION_FIELD_REQUIRED')
         .test('is-correct-file', 'VALIDATION_FIELD_FILE_BIG', checkIfFilesAreTooBig)
         .test(
           'is-big-file',
           'VALIDATION_FIELD_FILE_WRONG_TYPE',
           checkIfFilesAreCorrectType
         ),
})

Where the validation functions are:

export function checkIfFilesAreTooBig(files?: [File]): boolean {
  let valid = true
  if (files) {
    files.map(file => {
      const size = file.size / 1024 / 1024
      if (size > 10) {
        valid = false
      }
    })
  }
  return valid
}

export function checkIfFilesAreCorrectType(files?: [File]): boolean {
  let valid = true
  if (files) {
    files.map(file => {
      if (!['application/pdf', 'image/jpeg', 'image/png'].includes(file.type)) {
        valid = false
      }
    })
  }
  return valid
}
pfulop
  • 1,009
  • 13
  • 24
6
export const UploadFileSchema = yup.object().shape({
file: yup
    .mixed()
    .required("You need to provide a file")
    .test("fileSize", "The file is too large", (value) => {
        return value && value[0].sienter code hereze <= 2000000;
    })
    .test("type", "Only the following formats are accepted: .jpeg, .jpg, .bmp, .pdf and .doc", (value) => {
        return value && (
            value[0].type === "image/jpeg" ||
            value[0].type === "image/bmp" ||
            value[0].type === "image/png" ||
            value[0].type === 'application/pdf' ||
            value[0].type === "application/msword"
        );
    }),
});

This solution is taken from Maksim Ivanov (on youtube)

Moo
  • 3,369
  • 4
  • 22
  • 41
roniccolo
  • 99
  • 1
  • 5
  • 1
    I'm not sure about Formik but this didn't work with React Hook Form. The problem with RHF is that the file field will never be empty. Without files, it will contain an empty `FileList`. To check the `required` requirement you'd need to white your own validator function. Here is a [link](https://github.com/satansdeer/rhf-validate-file/blob/95e96d26e2645cb04554aa5f1ef679e70f42ecbf/src/App.js#L7-L18) to the file with this kind of validation implemented. – SatansDeer Nov 03 '21 at 15:03
4
  const SUPPORTED_FORMATS = ['image/jpg', 'image/jpeg', 'image/png'];

  const registerSchema = Yup.object().shape({
    uriImage: Yup.mixed()
      .nullable()
      .required('A file is required')
      .test('Fichier taille',
        'upload file', (value) => !value || (value && value.size <= 1024 * 1024))
      .test('format',
        'upload file', (value) => !value || (value && SUPPORTED_FORMATS.includes(value.type))),
  });
3

This code will work for validating image formats.

const SUPPORTED_FORMATS = ["image/jpg", "image/jpeg", "image/gif", "image/png"];

export const validateImageType = (value) => {
  if(value) {
    let type = value.match(/[^:]\w+\/[\w-+\d.]+(?=;|,)/)[0]
    return SUPPORTED_FORMATS.includes(type)
  }
}

  Yup.mixed() .test('fileSize', "File is too large", value => value.size <= FILE_SIZE) .test('fileType', "Your Error Message", value => SUPPORTED_FORMATS.includes(value.type) )
Musab Akram
  • 163
  • 10
3

image:

Yup.mixed().test(1000, "File Size is too large", value => value.size <= FILE_SIZE) .test('fileType', "Unsupported File Format", value => SUPPORTED_FORMATS.includes(['image/*']) )

https://hackernoon.com/formik-handling-files-and-recaptcha-209cbeae10bc

Jakub Kurdziel
  • 3,216
  • 2
  • 12
  • 22
AdekunleCodez
  • 120
  • 1
  • 1
  • 8
0

Based on pfulop's answer, here's how I had to do it, to make it work:

const maxFileSize = 800 * 1024 // 800 kilobytes
const fileTypes = ['image/png', 'image/jpg', 'image/jpeg']

const schema = yup.object().shape({
  first_name: yup.string().required(),
  last_name: yup.string().required(),
  email: yup.string().email().required(),
  avatar: yup.mixed().test('fileSize', appTranslation.ERROR_FILE_SIZE, (value) => {
    if (!value) return true
    return isFileSizeValid([value], maxFileSize)
  }).test('fileType', appTranslation.ERROR_FILE_TYPE, (value) => {
    if (!value) return true
    return isFileTypesValid([value], fileTypes)
  })
})

export function isFileSizeValid(files: [File], maxSize: number): boolean {
    let valid = true
    if (files) {
        files.map(file => {
            const fileSize = file.size
            if (fileSize > maxSize) {
                valid = false
            }
        })
    }
    return valid
}

export function isFileTypesValid(files: [File], authorizedExtensions: string[]): boolean {
    let valid = true
    if (files) {
        files.map(file => {
            if (!authorizedExtensions.includes(file.type)) {
                valid = false
            }
        })
    }
    return valid
}

I reuse functions in my code, which is why I've added maxSize & authorizedExtensions or the export keyword. The most important change is to const schema.

Elell
  • 15
  • 2