Валидация файлов и загрузок

Работа с файлами в веб-приложениях требует строгого контроля входящих данных, поскольку загрузка бинарного контента затрагивает безопасность, производительность и целостность системы. Библиотека Zod предоставляет единый подход к описанию и проверке структур данных, включая файлы, поступающие через формы, API или потоковые загрузки.

Базовая проверка файловых объектов

В браузерной среде файл представлен объектом File, который наследуется от Blob. Валидация таких объектов в Zod начинается с проверки типа экземпляра:

import { z } from "zod";

const fileSchema = z.instanceof(File);

Подобная схема гарантирует, что значение действительно является файлом, а не строкой, числом или произвольным объектом.

В серверной среде (например, Node.js) файлы часто представлены через библиотеки загрузки, такие как multer, где структура отличается. В таких случаях применяется более гибкая проверка через z.any() с последующей валидацией структуры вручную либо через z.object().

Проверка размера файла

Одним из ключевых параметров является ограничение по размеру. В Zod для этого используется метод refine, позволяющий добавлять пользовательские правила:

const fileWithSizeSchema = z.instanceof(File).refine(
  (file) => file.size <= 5 * 1024 * 1024,
  {
    message: "Размер файла превышает 5MB"
  }
);

Здесь логика проверки отделена от структуры, что сохраняет декларативность схемы.

Валидация MIME-типа

Контроль типа содержимого важен для предотвращения загрузки нежелательных форматов. MIME-тип доступен через file.type:

const imageFileSchema = z.instanceof(File).refine(
  (file) => ["image/jpeg", "image/png", "image/webp"].includes(file.type),
  {
    message: "Поддерживаются только изображения JPEG, PNG и WEBP"
  }
);

Подобная схема часто используется для аватаров, документов и медиафайлов.

Комбинированные проверки файлов

В реальных сценариях требуется одновременно проверять несколько характеристик: тип, размер и имя файла. Zod позволяет объединять условия через последовательные вызовы refine:

const uploadSchema = z.instanceof(File)
  .refine(file => file.size <= 10 * 1024 * 1024, {
    message: "Файл слишком большой"
  })
  .refine(file => file.type === "application/pdf", {
    message: "Допустим только PDF"
  })
  .refine(file => file.name.endsWith(".pdf"), {
    message: "Некорректное расширение файла"
  });

Такой подход сохраняет читаемость схемы и позволяет расширять правила без изменения структуры.

Валидация массива файлов

При множественной загрузке данные поступают в виде массива файлов. Zod предоставляет композицию схем через array:

const multipleFilesSchema = z.array(
  z.instanceof(File).refine(file => file.size <= 2 * 1024 * 1024, {
    message: "Каждый файл должен быть не более 2MB"
  })
);

Валидация применяется к каждому элементу массива независимо, обеспечивая единообразные ограничения.

Проверка через transform и нормализация данных

Иногда требуется не только проверка, но и преобразование структуры файла в более удобный формат. В таких случаях используется transform:

const normalizedFileSchema = z.instanceof(File).transform((file) => ({
  name: file.name,
  size: file.size,
  type: file.type
}));

Результатом становится нормализованный объект, пригодный для хранения или передачи в API.

Работа с FormData

Загрузки через HTTP часто приходят в виде FormData. Zod не парсит его напрямую, но может валидировать извлечённые значения:

const formSchema = z.object({
  file: z.instanceof(File),
  description: z.string().min(1)
});

После извлечения данных из FormData структура передаётся в safeParse:

const data = {
  file: formData.get("file"),
  description: formData.get("description")
};

const result = formSchema.safeParse(data);

Такой подход позволяет централизовать контроль входных данных.

Кастомные ошибки и детализация валидации

Zod поддерживает детализированные сообщения об ошибках, что особенно важно при работе с файлами:

const schema = z.instanceof(File).refine(
  (file) => file.size > 0,
  {
    message: "Пустой файл недопустим",
    path: ["file"]
  }
);

Поле path позволяет точно указать источник ошибки внутри структуры данных.

Условная валидация файлов

В сложных формах требования к файлу могут зависеть от других полей:

const schema = z.object({
  type: z.enum(["avatar", "document"]),
  file: z.instanceof(File)
}).refine((data) => {
  if (data.type === "avatar") {
    return data.file.size <= 1 * 1024 * 1024;
  }
  return true;
}, {
  message: "Аватар не должен превышать 1MB"
});

Подобная логика позволяет связывать бизнес-правила с файловыми ограничениями.

Асинхронная валидация файлов

Некоторые проверки требуют обращения к внешним сервисам, например анализ содержимого файла. Zod поддерживает асинхронные схемы:

const asyncSchema = z.instanceof(File).refine(async (file) => {
  const buffer = await file.arrayBuffer();
  return buffer.byteLength > 100;
});

Такая модель применяется при антивирусной проверке, анализе изображений или чтении метаданных.

Ошибки безопасного разбора данных

Метод safeParse используется для предотвращения исключений при невалидных данных:

const result = fileSchema.safeParse(input);

if (!result.success) {
  console.log(result.error.format());
}

Структурированные ошибки позволяют точно определить причину отказа валидации.

Ограничения и особенности файловых схем

Работа с файлами в Zod зависит от окружения. В браузере доступен полноценный объект File, тогда как в Node.js требуется адаптация входных данных. В серверных приложениях часто используется промежуточный слой, приводящий данные к единому виду перед применением схем.

Особое внимание требуется уделять сериализации: объекты File не преобразуются напрямую в JSON, поэтому для передачи данных между слоями используется трансформация или извлечение метаданных.

Валидация потоковых загрузок

При работе с потоками данных файлы могут поступать частями. В таких случаях Zod используется для проверки итоговой структуры после сборки:

const chunkedSchema = z.object({
  filename: z.string(),
  chunks: z.array(z.instanceof(Uint8Array)),
  totalSize: z.number()
});

Проверка гарантирует целостность собранного файла перед сохранением.