Интеграция Sharp (Node.js) для серверной обработки изображений

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Интеграция Sharp (Node.js) для серверной обработки изображений
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Интеграция Sharp (Node.js) для серверной обработки изображений

Sharp — Node.js-библиотека на базе libvips, на порядок быстрее Jimp или Canvas API. Обрабатывает JPEG, PNG, WebP, AVIF, TIFF, GIF, SVG без потери качества, не требует ImageMagick и работает в 4–5 раз быстрее аналогов при вдвое меньшем потреблении памяти.

Установка и базовая настройка

npm install sharp
# Sharp поставляется с предкомпилированными бинарниками libvips
# для Linux x64, macOS arm64, Windows x64

Sharp использует потоковую обработку — изображение не загружается полностью в память:

const sharp = require('sharp')

// Базовый pipeline: resize + WebP + сохранение
await sharp('./input/photo.jpg')
  .resize(800, 600, {
    fit: 'inside',           // вписать в рамку без обрезки
    withoutEnlargement: true // не увеличивать маленькие изображения
  })
  .webp({ quality: 82, effort: 4 })
  .toFile('./output/photo.webp')

Форматы вывода и параметры качества

Формат Метод Рекомендуемое качество
JPEG .jpeg({ quality, mozjpeg }) 80–85, mozjpeg: true
WebP .webp({ quality, effort }) 80–85, effort: 4
AVIF .avif({ quality, effort }) 50–60, effort: 4
PNG .png({ compressionLevel }) compressionLevel: 6–8
// Генерация нескольких форматов из одного источника
async function convertToModernFormats(inputPath, outputDir, baseName) {
  const image = sharp(inputPath)
  const meta = await image.metadata()

  const resized = image.resize(1200, null, {
    fit: 'inside',
    withoutEnlargement: true
  })

  await Promise.all([
    // WebP для современных браузеров
    resized.clone()
      .webp({ quality: 82, effort: 4 })
      .toFile(`${outputDir}/${baseName}.webp`),

    // AVIF для Chrome/Firefox
    resized.clone()
      .avif({ quality: 55, effort: 4 })
      .toFile(`${outputDir}/${baseName}.avif`),

    // JPEG как fallback
    resized.clone()
      .jpeg({ quality: 85, mozjpeg: true })
      .toFile(`${outputDir}/${baseName}.jpg`),
  ])

  return { width: meta.width, height: meta.height }
}

Обработка EXIF и ориентации

Sharp автоматически читает EXIF-ориентацию, но не применяет её по умолчанию:

const image = sharp(buffer)
  .rotate()           // auto-rotate по EXIF orientation
  .withMetadata({     // сохранить метаданные (кроме GPS если нужна приватность)
    exif: {
      IFD0: { Copyright: 'My Company 2024' }
    }
  })

Для удаления GPS-данных при публичной публикации:

// Удалить все метаданные (EXIF, IPTC, XMP)
.withMetadata(false)
// Или оставить только ICC-профиль для корректных цветов
.withMetadata({ icc: true })

Интеграция с Multer (Express)

const multer = require('multer')
const { v4: uuidv4 } = require('uuid')

// Хранить в памяти, обрабатывать Sharp'ом до сохранения на диск/S3
const upload = multer({ storage: multer.memoryStorage() })

app.post('/api/upload', upload.single('image'), async (req, res) => {
  if (!req.file) return res.status(400).json({ error: 'No file' })

  // Валидировать формат через metadata (не MIME-заголовок — его можно подделать)
  let meta
  try {
    meta = await sharp(req.file.buffer).metadata()
  } catch {
    return res.status(422).json({ error: 'Invalid image' })
  }

  const allowedFormats = ['jpeg', 'png', 'webp', 'gif', 'avif']
  if (!allowedFormats.includes(meta.format)) {
    return res.status(422).json({ error: `Format ${meta.format} not allowed` })
  }

  const id = uuidv4()
  const variants = await processAndUpload(req.file.buffer, id)

  res.json({ id, variants })
})

async function processAndUpload(buffer, id) {
  const image = sharp(buffer).rotate() // EXIF auto-rotate

  const sizes = {
    thumb:  { width: 150, height: 150, fit: 'cover' },
    medium: { width: 800 },
    large:  { width: 1920 }
  }

  const results = {}

  for (const [name, dims] of Object.entries(sizes)) {
    const processed = await image.clone()
      .resize(dims.width, dims.height || null, {
        fit: dims.fit || 'inside',
        withoutEnlargement: true
      })
      .webp({ quality: 82, effort: 4 })
      .toBuffer()

    const key = `images/${id}/${name}.webp`
    await s3.putObject({
      Bucket: process.env.S3_BUCKET,
      Key: key,
      Body: processed,
      ContentType: 'image/webp',
      CacheControl: 'public, max-age=31536000, immutable'
    }).promise()

    results[name] = key
  }

  return results
}

Водяной знак и наложение

async function addWatermark(imageBuffer, watermarkPath) {
  const image = sharp(imageBuffer)
  const { width, height } = await image.metadata()

  // Масштабировать watermark под 20% ширины изображения
  const wmSize = Math.floor(width * 0.2)

  const watermark = await sharp(watermarkPath)
    .resize(wmSize)
    .toBuffer()

  return image
    .composite([{
      input: watermark,
      gravity: 'southeast',
      blend: 'over'
    }])
    .toBuffer()
}

Производительность: concurrency и потоки

Sharp по умолчанию использует все CPU. В production ограничивают:

sharp.concurrency(2) // максимум 2 потока libvips

// Для высокой нагрузки — очередь через p-limit
const pLimit = require('p-limit')
const limit = pLimit(4) // 4 параллельных задачи

const tasks = images.map(img =>
  limit(() => processImage(img))
)
const results = await Promise.all(tasks)

Обработка ошибок

async function safeProcess(buffer) {
  try {
    const meta = await sharp(buffer).metadata()

    // Защита от огромных изображений (decompression bomb)
    if (meta.width * meta.height > 50_000_000) {
      throw new Error('Image too large: exceeds 50MP limit')
    }

    return await sharp(buffer)
      .resize(2000, 2000, { fit: 'inside', withoutEnlargement: true })
      .webp({ quality: 82 })
      .toBuffer()

  } catch (err) {
    if (err.message.includes('Input buffer contains unsupported image format')) {
      throw new TypeError('Unsupported image format')
    }
    throw err
  }
}

Срок выполнения

Интеграция Sharp с Multer, несколькими форматами вывода и загрузкой в S3 — 1–2 рабочих дня.