В экосистеме JavaScript-валидации обработка файловых данных относится к числу наиболее чувствительных сценариев, поскольку требует одновременного контроля структуры объекта, его происхождения, метаданных и содержимого. В связке с резолверами схем валидации YupResolver используется для интеграции декларативных правил проверки с формами и API, где File-объекты передаются как часть состояния.
File в браузере представляет собой экземпляр File,
унаследованный от Blob, и содержит ключевые поля:
name, size, type,
lastModified. Эти параметры становятся основой для
валидации, поскольку именно они позволяют определить допустимость
загружаемого содержимого до отправки на сервер.
При работе с файловыми полями в схемах важно учитывать несколько уровней проверки:
С точки зрения Yup-схемы File-объект обычно трактуется как
mixed тип, что позволяет применять к нему произвольные
правила трансформации и тестирования.
Типовая схема начинается с определения поля как mixed,
после чего добавляются ограничения через test.
import * as Yup from "yup";
const schema = Yup.object({
file: Yup.mixed()
.required("Файл обязателен")
.test("fileSize", "Файл слишком большой", (value) => {
return value && value.size <= 5 * 1024 * 1024;
})
.test("fileType", "Неподдерживаемый формат", (value) => {
return value && ["image/jpeg", "image/png"].includes(value.type);
})
});
Здесь File-объект проверяется по двум ключевым параметрам: размер и MIME-тип. Подобный подход характерен для большинства фронтенд-форм, где требуется предварительная фильтрация до отправки данных.
File-объекты имеют особенность: отсутствие значения может выражаться
не только как null, но и как undefined или
пустой массив в случае множественной загрузки.
Валидация обязательности должна учитывать все возможные состояния:
Yup.mixed().test(
"required-file",
"Файл не выбран",
(value) => value instanceof File
);
При использовании более сложных форм часто добавляется дополнительная защита от ложноположительных значений:
Yup.mixed().test(
"not-empty",
"Файл отсутствует",
(value) => {
if (!value) return false;
if (Array.isArray(value)) return value.length > 0;
return value instanceof File;
}
);
Ограничение размера — одна из наиболее частых задач. Оно реализуется
через проверку свойства size, которое выражается в
байтах.
const MAX_SIZE = 10 * 1024 * 1024;
const fileSizeSchema = Yup.mixed().test(
"max-size",
"Превышен допустимый размер файла",
(file) => {
if (!file) return false;
return file.size <= MAX_SIZE;
}
);
В сложных системах ограничения могут зависеть от типа файла:
const sizeRules = {
image: 5 * 1024 * 1024,
video: 50 * 1024 * 1024,
document: 10 * 1024 * 1024
};
const schema = Yup.mixed().test(
"dynamic-size",
"Файл превышает допустимый размер",
(file) => {
if (!file) return false;
const category = file.type.startsWith("image/")
? "image"
: file.type.startsWith("video/")
? "video"
: "document";
return file.size <= sizeRules[category];
}
);
Проверка file.type позволяет ограничивать допустимые
форматы загрузки. Это особенно важно при работе с изображениями и
документами.
const allowedTypes = ["image/png", "image/jpeg", "application/pdf"];
const schema = Yup.mixed().test(
"file-type",
"Недопустимый тип файла",
(file) => {
if (!file) return false;
return allowedTypes.includes(file.type);
}
);
Следует учитывать, что MIME-тип может отсутствовать или быть подделан на клиентской стороне, поэтому такая проверка должна дополняться серверной валидацией.
Расширение имени файла используется как дополнительный уровень фильтрации:
const allowedExtensions = ["png", "jpg", "jpeg"];
const schema = Yup.mixed().test(
"extension",
"Недопустимое расширение файла",
(file) => {
if (!file || !file.name) return false;
const ext = file.name.split(".").pop().toLowerCase();
return allowedExtensions.includes(ext);
}
);
Расширение не является надежным источником истины, но часто используется как UX-ограничение.
При множественной загрузке поле содержит массив
File-объектов. В этом случае схема усложняется, так как
требуется проверять каждый элемент.
const schema = Yup.object({
files: Yup.array()
.of(
Yup.mixed().test(
"file-check",
"Некорректный файл",
(file) => file instanceof File
)
)
.min(1, "Необходимо загрузить хотя бы один файл")
});
Дополнительно можно применять агрегационные проверки:
Yup.array().test(
"total-size",
"Общий размер файлов превышает лимит",
(files) => {
if (!files) return false;
const total = files.reduce((acc, f) => acc + f.size, 0);
return total <= 20 * 1024 * 1024;
}
);
File-объекты могут требовать анализа содержимого, особенно при работе с изображениями или текстовыми документами. В Yup это реализуется через асинхронные тесты.
const schema = Yup.mixed().test(
"image-dimensions",
"Недопустимые размеры изображения",
async (file) => {
if (!file) return false;
const img = await loadImage(file);
return img.width >= 300 && img.height >= 300;
}
);
function loadImage(file) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onl oad = () => resolve(img);
img.oner ror = reject;
img.src = URL.createObjectURL(file);
});
}
Асинхронная валидация особенно важна при проверке медиа-контента, так как требует предварительной загрузки данных в память.
При использовании форменных библиотек схема Yup подключается через резолвер, обеспечивая автоматическую обработку ошибок и состояния поля. В связке с YupResolver схема превращается в единый слой валидационной логики.
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
const form = useForm({
resolver: yupResolver(schema)
});
File-объекты при этом проходят через схему без дополнительной ручной обработки, что позволяет централизовать правила проверки.
Иногда требуется нормализовать входные данные до применения тестов. Например, привести одиночный файл к массиву:
Yup.mixed().transform((value) => {
if (value && !Array.isArray(value)) {
return [value];
}
return value;
});
Подобные трансформации полезны при унификации поведения одиночных и множественных загрузок.
Реальные сценарии редко ограничиваются одной проверкой. Обычно File-объект проходит цепочку условий:
const schema = Yup.mixed()
.required("Файл обязателен")
.test("type", "Неверный тип", checkType)
.test("size", "Слишком большой файл", checkSize)
.test("name", "Некорректное имя файла", checkName)
.test("content", "Файл не прошел проверку содержимого", checkContent);
Такой подход позволяет разделить ответственность между независимыми функциями, каждая из которых отвечает за свой аспект проверки.
При работе с File-объектами часто возникают нетривиальные ситуации:
undefined, а не
как пустой массивfile.type может быть пустой строкойsize может быть равен 0 для пустых файловЭти особенности требуют явной защиты в каждом тесте, чтобы избежать ложных ошибок валидации.
Стабильная схема валидации файлов опирается на принцип многослойной проверки:
Каждый слой реализуется отдельным тестом, что обеспечивает прозрачность и предсказуемость поведения всей схемы.