Гид по технологиям

Multer: загрузка изображений в Node.js — пошаговое руководство

8 min read Node.js Обновлено 06 Jan 2026
Multer: загрузка изображений в Node.js
Multer: загрузка изображений в Node.js

Буквенные блоки, составляющие слово «JAVASCRIPT»

Есть три основных подхода к работе с загрузкой файлов в Node.js: сохранять изображения прямо на сервере, хранить бинарные или base64-данные в базе, либо использовать хранилище объектов (например, AWS S3) для управления изображениями. В этой статье мы подробно рассмотрим первый подход — хранение файлов на локальном сервере с помощью middleware Multer.

Зачем использовать Multer

Multer — middleware для Express, который упрощает обработку multipart/form-data (форм, содержащих файлы). Он строится поверх парсера busboy и эффективен при потоковой передаче данных из формы на диск или в память.

Ключевые преимущества Multer:

  • Простая интеграция с Express.
  • Поддержка сохранения на диск или в память.
  • Встроенные опции для ограничения размера и фильтрации по типу.

Важно: Multer сам по себе не сканирует файлы на вредоносный код и не обеспечивает шифрование хранения — эти задачи нужно решать отдельно.

Что вы получите в итоге

  • Рабочий пример проекта с app.js.
  • Настройки storage и валидаторы (size, mime, extension).
  • Маршруты для одиночной и множественной загрузки.
  • Рекомендации по безопасности, тестированию и развёртыванию.

Шаг 1: Подготовка окружения разработки

Код из этого руководства можно разместить в любом репозитории. Здесь показан минимальный набор команд для создания проекта и установки зависимостей.

Создайте папку проекта и перейдите в неё:

mkdir multer-tutorial
cd multer-tutorial

Инициализируйте npm:

npm init -y

Установите зависимости — Express и Multer:

npm install express multer

Создайте файл app.js в корне проекта и добавьте минимальную заготовку сервера:

// app.js
const express = require('express');
const app = express();

const port = process.env.PORT || 3000;

app.listen(port, ()=>{
    console.log(`App is listening on port ${port}`);
});

Совет: добавьте .gitignore для node_modules и папки с изображениями, если не хотите хранить файлы в репозитории.

Шаг 2: Конфигурация Multer — storage engine

Импортируйте multer и настройте storage engine, который управляет тем, куда сохраняются файлы и как они именуются.

const multer = require("multer");

Multer предоставляет метод diskStorage, который принимает объект с параметрами destination и filename.

Добавьте в app.js следующий блок:

// Setting storage engine
const storageEngine = multer.diskStorage({
  destination: "./images",
  filename: (req, file, cb) => {
    cb(null, `${Date.now()}--${file.originalname}`);
  },
});

// initializing multer
const upload = multer({
  storage: storageEngine,
});

Пояснения:

  • destination — путь в файловой системе относительно рабочей директории процесса Node.js; в примере — ./images.
  • filename — функция, которая должна вызвать cb(error, filename). В примере используется метка времени Date.now() для получения уникального имени.

Рекомендуется заранее создать папку images и установить корректные права доступа:

mkdir images
chmod 755 images

Можно программно создавать папку при старте приложения, если её нет:

const fs = require('fs');
const imagesDir = './images';
if (!fs.existsSync(imagesDir)) {
  fs.mkdirSync(imagesDir, { recursive: true });
}

Шаг 3: Правила валидации изображений

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

Пример добавления лимита размера (в байтах) и фильтра файлов:

const path = require("path");

const checkFileType = function (file, cb) {
  // Разрешённые расширения
  const fileTypes = /jpeg|jpg|png|gif|svg/;

  // Проверка расширения файла
  const extName = fileTypes.test(path.extname(file.originalname).toLowerCase());

  // Проверка MIME-типа
  const mimeType = fileTypes.test(file.mimetype);

  if (mimeType && extName) {
    return cb(null, true);
  } else {
    cb(new Error("Error: You can Only Upload Images!!"));
  }
};

const upload = multer({
  storage: storageEngine,
  limits: { fileSize: 10000000 }, // 10 MB
  fileFilter: (req, file, cb) => {
    checkFileType(file, cb);
  },
});

Примечание: в примере выше limit установлен в 10 000 000 байт (примерно 10 МБ). Подбирайте лимит в зависимости от требований приложения.

Важно: использование только проверки расширения недостаточно — MIME-тип и, при необходимости, дополнительная проверка содержимого (проверка сигнатур файлов) помогут уменьшить риск загрузки неподходящих файлов.

Шаг 4: Multer как middleware в маршрутах Express

Multer предоставляет несколько методов для обработки файлов:

  • upload.single(fieldname) — одиночный файл
  • upload.array(fieldname, maxCount) — массив файлов с одинаковым именем поля
  • upload.fields([{ name: ‘avatar’, maxCount: 1 }, { name: ‘gallery’, maxCount: 8 }]) — разные поля
  • upload.any() — принимает файлы из любых полей (не рекомендуется без валидации)

Примеры маршрутов:

app.post("/single", upload.single("image"), (req, res) => {
  if (req.file) {
    res.send("Single file uploaded successfully");
  } else {
    res.status(400).send("Please upload a valid image");
  }
});

app.post("/multiple", upload.array("images", 5), (req, res) => {
  if (req.files) {
    res.send("Multiple files uploaded successfully");
  } else {
    res.status(400).send("Please upload valid images");
  }
});

Пример HTML-формы для тестирования (одиночная загрузка):



И для множественной загрузки:



Обработка ошибок и ответы клиенту

Multer генерирует ошибки при превышении лимита размера (LIMIT_FILE_SIZE) и при фильтрации файлов. Рекомендуется централизованно обрабатывать ошибки middleware:

app.post('/single', (req, res, next) => {
  upload.single('image')(req, res, function (err) {
    if (err) {
      // Обработка ошибок Multer
      return res.status(400).send({ error: err.message });
    }
    if (!req.file) return res.status(400).send({ error: 'No file uploaded' });
    res.send({ message: 'Uploaded', file: req.file.filename });
  });
});

Также можно добавить глобальный обработчик ошибок Express для отлова неявных ошибок.

Как отдавать загруженные файлы клиентам

Если вы хотите позволить клиентам просматривать загруженные изображения, можно использовать express.static:

app.use('/images', express.static('images'));

После этого файл, сохранённый как ./images/1663080276614–example.jpg, будет доступен по URL http://:/images/1663080276614–example.jpg.

Важно: отключите индексирование директорий и убедитесь, что доступ предоставлен только к ожидаемым ресурсам.

Практические варианты использования и альтернативы

Когда хранить файлы на локальном диске подходит:

  • Малые проекты или внутренняя система с одной нодой.
  • Когда требования к быстрому доступу простые и у вас есть резервное копирование.

Когда лучше использовать облачное хранилище (S3, GCS и т. п.):

  • Масштабирование на несколько экземпляров приложений.
  • Требуется CDN, управление версионированием, долговременное хранение.

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

Краткое сравнение (качественно):

  • Локальный диск: просто, дешево, но ограничено в масштабировании и надёжности.
  • S3/облако: масштабируется, интегрируется с CDN, требует дополнительных затрат и конфигурации.
  • База данных: медленнее и тяжело масштабируется, но может упростить некоторые транзакции.

Безопасность и харднинг

Рекомендации при работе с загрузками:

  • Ограничьте типы файлов и размер.
  • Генерируйте безопасные имена файлов (не используйте оригинальные имена без проверки).
  • Сканируйте загружаемые файлы антивирусом в критичных приложениях.
  • Ограничьте права на папку (например, 750/755 по необходимости).
  • Не храните исполняемые файлы в публичной директории.
  • Используйте HTTPS для передачи файлов.
  • Разграничивайте доступ к файлам через авторизацию и подписи URL, если это нужно.

Конфиденциальность и соответствие (GDPR и прочее)

Если загружаются файлы пользователей, учтите:

  • Храните минимально необходимые метаданные и проработайте срок хранения контента.
  • Реализуйте функционал удаления данных по запросу пользователя, если это требуется.
  • Документируйте причину хранения и срок хранения (retention policy).
  • Шифруйте резервные копии, содержащие пользовательские файлы.

Рольные чек-листы

Разделим ответственность между командами:

  • Разработчик:

    • Настроил Multer и валидацию типов/размера.
    • Обработал ошибки и вернул ясные сообщения клиенту.
    • Написал unit/integration тесты для загрузки/валидации.
  • DevOps/Инфраструктура:

    • Настроил резервное копирование папки images или указал облачное хранилище.
    • Настроил права доступа и мониторинг дискового пространства.
  • Безопасность:

    • Проверил и протестировал ограничения по типу и размеру.
    • Добавил проверку сигнатур файлов и/или интеграцию с антивирусом при необходимости.
  • QA:

    • Проверил позитивные и негативные сценарии (см. тесты ниже).
    • Убедился, что ошибки возвращаются корректно и содержат понятные сообщения.

Критерии приёмки и тестовые сценарии

Критерии приёмки (acceptance):

  • Можно загрузить одно изображение и получить успешный ответ.
  • Можно загрузить несколько изображений (до указанного максимума).
  • При попытке загрузить файл неподдерживаемого формата возвращается ошибка с кодом 400.
  • При загрузке файла, превышающего лимит, возвращается ошибка LIMIT_FILE_SIZE.
  • Загруженные файлы доступны по статическому маршруту (/images/… ) с корректными правами доступа.

Тестовые сценарии:

  1. Позитивный: загрузка JPEG 200 KB — статус 200, файл сохранён.
  2. Позитивный: множественная загрузка 3 PNG — статус 200, 3 файла сохранены.
  3. Негативный: загрузка .exe — статус 400, сообщение о неверном формате.
  4. Негативный: загрузка файла 50 MB (если лимит 10 MB) — ошибка LIMIT_FILE_SIZE.
  5. Интеграционный: доступ по URL к сохранённому файлу — возвращён корректный content-type и статус 200.

Сниппет: коллекция команд и методов Multer (cheat sheet)

  • upload.single(‘avatar’) — один файл из поля avatar.
  • upload.array(‘photos’, 5) — до 5 файлов из поля photos.
  • upload.fields([{ name: ‘avatar’, maxCount: 1 }, { name: ‘gallery’, maxCount: 8 }]) — разные поля одновременно.
  • upload.any() — принимает файлы из любых полей (не рекомендуется без валидации).

Проверка ошибок Multer в middleware:

app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    // Ошибки Multer (например, LIMIT_FILE_SIZE)
    return res.status(400).json({ error: err.code });
  } else if (err) {
    return res.status(500).json({ error: err.message });
  }
  next();
});

Полный пример app.js (собранный)

// app.js
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');

const app = express();
const port = process.env.PORT || 3000;

// Создаём папку images, если её нет
const imagesDir = './images';
if (!fs.existsSync(imagesDir)) {
  fs.mkdirSync(imagesDir, { recursive: true });
}

// Storage engine
const storageEngine = multer.diskStorage({
  destination: imagesDir,
  filename: (req, file, cb) => {
    // Можно дополнительно санитизировать file.originalname
    const safeName = file.originalname.replace(/[^a-zA-Z0-9.\-\_]/g, '_');
    cb(null, `${Date.now()}--${safeName}`);
  },
});

// check file type
const checkFileType = function (file, cb) {
  const fileTypes = /jpeg|jpg|png|gif|svg/;
  const extName = fileTypes.test(path.extname(file.originalname).toLowerCase());
  const mimeType = fileTypes.test(file.mimetype);
  if (mimeType && extName) {
    return cb(null, true);
  } else {
    cb(new Error('Only image files are allowed'));
  }
};

const upload = multer({
  storage: storageEngine,
  limits: { fileSize: 10 * 1024 * 1024 }, // 10 MB
  fileFilter: (req, file, cb) => {
    checkFileType(file, cb);
  },
});

// Статическая раздача изображений
app.use('/images', express.static(imagesDir));

// Маршрут одиночной загрузки
app.post('/single', (req, res) => {
  upload.single('image')(req, res, function (err) {
    if (err) {
      if (err instanceof multer.MulterError) {
        return res.status(400).json({ error: err.code });
      }
      return res.status(400).json({ error: err.message });
    }
    if (!req.file) return res.status(400).json({ error: 'No file uploaded' });
    res.json({ message: 'Uploaded', filename: req.file.filename });
  });
});

// Маршрут множественной загрузки
app.post('/multiple', (req, res) => {
  upload.array('images', 5)(req, res, function (err) {
    if (err) {
      if (err instanceof multer.MulterError) {
        return res.status(400).json({ error: err.code });
      }
      return res.status(400).json({ error: err.message });
    }
    if (!req.files || req.files.length === 0) return res.status(400).json({ error: 'No files uploaded' });
    res.json({ message: 'Uploaded', files: req.files.map(f => f.filename) });
  });
});

// Глобальный обработчик ошибок
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: 'Internal server error' });
});

app.listen(port, () => {
  console.log(`App is listening on port ${port}`);
});

Когда Multer может не подойти

  • Если приложение распределено на множество инстансов без общего хранилища — локальное сохранение неудобно.
  • Если требуется высокодоступное, геораспределённое хранение и CDN — лучше S3/Cloud Storage.
  • Если нужно атомарно хранить бинарные данные вместе с транзакциями БД — может подойти хранение в базе, но это редко оптимально.

Ментальные модели и эвристики

  • Правило простоты: для небольших приложений сначала начните с Multer + локальное хранение; при росте архитектуры мигрируйте на облако.
  • Принцип минимальных прав: папка с файлами должна иметь минимально необходимые права и не должна содержать исполняемых файлов.
  • Отделяйте метаданные (в БД) и файлы (на диске/в S3). В БД храните путь/URL, тип, размер, uploaderId и срок хранения.

План миграции с локального хранилища на S3 (высокоуровнево)

  1. Добавьте хранение в S3 при загрузке: вместо diskStorage используйте потоковую отправку в S3.
  2. Продублируйте логику хранения — записывайте URL в вашу БД, пока не переключитесь полностью.
  3. Перенесите существующие файлы в S3 (bulk upload) и обновите записи в БД.
  4. После проверки отключите локальную раздачу и удалите локальные файлы.

Факто-бокс — ключевые настройки (для справки)

  • fileSize: задаётся в байтах; 1 MB = 1 048 576 байт (приблизительно), в примерах часто используют 1 000 000 или 10 000 000 байт.
  • maxCount для upload.array: ограничивает количество файлов в одной форме.
  • Поля multer: storage, limits, fileFilter.

Короткое объявление для команды (100–200 слов)

В проект добавлен обработчик загрузки изображений на основе Multer. Он поддерживает одиночную и множественную загрузку, проверку типа (jpeg, jpg, png, gif, svg) и ограничение размера файлов (по умолчанию 10 МБ). Изображения сохраняются в ./images с безопасными именами, доступными по маршруту /images/. Добавлены рекомендации по безопасности: проверка расширения и MIME-типа, санитизация имён, права доступа к папке и централизованная обработка ошибок. Для масштабируемых случаев рекомендовано перенести хранение в S3 и хранить в БД только URL/метаданные.


В конце: приёмка, тесты и план миграции помогут команде быстро внедрить и при необходимости масштабировать решение. Если нужно — могу дополнительно подготовить пример интеграции с AWS S3 или сценарии CI/CD для автоматической очистки старых изображений.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство