Node.js: изменение размера изображения без ImageMagick

86

Я разрабатываю веб-приложение на Node.js (+ express 4), где пользователи могут установить изображение своего профиля, загрузив его на сервер. Мы уже ограничили mimetype файла и максимальный размер файла, поэтому пользователь не может загружать более 200 КБ изображений в формате PNG или JPEG.

Проблема в том, что мы хотели бы изменить размер (на стороне сервера) загруженного изображения до 200x200, чтобы улучшить загрузку страницы и сэкономить место на диске. После некоторого исследования все ответы указали на использование любого модуля на основе ImageMagick или GraphicsMagick.

Однако необходимость установки ImageMagick / GraphicsMagick для простого изменения размера изображения кажется мне слишком излишней, поэтому есть ли другое решение, кроме этого, для Node.js?

Изменить: я изменил принятое решение на острое, поскольку предыдущее решение (lwip) больше не поддерживается. Спасибо за все ваши отзывы!

zacr0
источник
Привет. У меня вопрос Как уменьшить размер изображения до 200 КБ? Расскажите, пожалуйста, о пути. Благодарю.
C.Petrescu
Здравствуйте, этот вопрос стоит опубликовать как новый, если вы не найдете ни одного связанного с ним, который был ранее опубликован. Чтобы дать вам немного света, попробуйте найти методы сжатия и изменения размера в API, предоставленном для инструментов, которые вы можете найти в этом вопросе.
zacr0

Ответы:

92

Я бы проголосовал за острое :

sharp('input.jpg')
  .resize(200, 200)
  .toFile('ouput.jpg', function(err) {
    // output.jpg is a 200 pixels wide and 200 pixels high image
    // containing a scaled and cropped version of input.jpg
  });

Это быстро, обычно в 6 раз быстрее, чем самые быстрые привязки узлов на основе imagemagick , и работает с очень небольшим объемом памяти, возможно, в 10 раз меньше . четкие ссылки на библиотеку изображений libvips напрямую, нет необходимости использовать внешнюю программу, а сама библиотека быстрее и эффективнее, чем * magick в этой задаче. Он поддерживает такие полезные вещи, как поток, буфер и ввод и вывод файловой системы, управление цветом, прозрачность, обещания, наложения, WebP, SVG и многое другое.

Начиная с версии Sharp 0.20, npm будет автоматически загружать полные предварительно скомпилированные двоичные файлы на большинстве платформ, поэтому в node-gyp нет необходимости. Просто введите:

npm install sharp

или же:

yarn add sharp

И вперед.

Jcupitt
источник
7
Начиная с версии v0.12.0, sharpбольше не имеет внешних зависимостей времени выполнения для пользователей Linux и Windows, так как в него входит предварительно скомпилированная версия libvips. Вы обнаружите, что операции изменения размера примерно в 10 раз быстрее, чем LWIP, и занимают лишь часть памяти.
Ловелл Фуллер
4
Sharp
jcupitt 01
1
Это сотни файлов, но все они автоматически управляются npm, вам не нужно об этом думать.
jcupitt 05
4
@CoDEmanX Возможно, npm install --global --production windows-build-toolsсначала попробуйте запустить . См. Также github.com/Microsoft/nodejs-guidelines/blob/master/…
Ловелл Фуллер,
1
Я создал небольшой скрипт, который позволяет легко увидеть, что происходит и как это будет выглядеть в контенте, таком как карточки ускорения и фон: обложка для лучшей оптимизации параметров, возможно, это интересное github.com/lcherone/sharp-test
Лоуренс Чероне
71

Недавно я начал разрабатывать модуль обработки изображений для NodeJS без каких-либо зависимостей во время выполнения ( прочтите, почему ). Это все еще на ранней стадии, но уже можно использовать.

То, что вы просите, будет сделано следующим образом:

image.resize(200, 200, function(err, image){
    // encode resized image to jpeg and get a Buffer object
    image.toBuffer('jpg', function(err, buffer){
        // save buffer to disk / send over network / etc.
    });
});

Дополнительная информация в репозитории Github модуля .

EyalAr
источник
7
Ваш модуль потрясающий. Однако для этого требуется так много памяти. Я пытался writeFile с 1.8Mbизображением, и это требует 130MB памяти. После этого я 4MB, 11000x6000провожу тест, изменяя размер изображения до нескольких миниатюр (640 560 480, ..., 160), и это занимает примерно 1,7 ГБ памяти. Это ошибка?
Льюис
2
Привет, @Orion, спасибо за отзыв. Пожалуйста, перейдите в репозиторий Github и откройте проблему с более подробной информацией (ОС, версии, код для воспроизведения этого). Мы постараемся решить это вместе :)
EyalAr
@EyalAr Эй, Эй, как изменить размер и сохранить аспект? (изменить размер до максимально возможного размера по ширине, высоте) без растяжения
Дэниел Кром
10
Я бы посоветовал не использовать lwip в 2017 году. Кажется, этот пакет больше не поддерживается, и у него есть серьезные проблемы с установкой в ​​Windows, а теперь даже на платформах Unix.
zerefel
9
lwip, к сожалению, мертвый проект. резкий, однако все еще, кажется, активно поддерживается.
Лоран
16

Взгляните на lwip: https://github.com/EyalAr/lwip

Очень просто и удобно

npm install lwip

а затем в коде вашего узла

// obtain an image object:
require('lwip').open('image.jpg', function(err, image){

  // check err...
  // define a batch of manipulations and save to disk as JPEG:
  image.batch()
    .scale(0.75)          // scale to 75%
    .rotate(45, 'white')  // rotate 45degs clockwise (white fill)
    .crop(200)            // crop a 200X200 square from center
    .blur(5)              // Gaussian blur with SD=5
    .writeFile('output.jpg', function(err){
      // check err...
      // done.
    });

});

Я успешно реализовал это в моем загрузчике файлов, и он работает как шарм.

Арвинд
источник
1
Это то, что я искал, без необходимости устанавливать излишние тяжелые внешние зависимости для пары функций, связанных с изменением размера изображения.
zacr0
3
Кстати, @EyalAr является автором этого модуля узла. Его комментарий также приведен ниже.
Arvind
Очень интуитивно понятен в установке и работе. Мне очень нравится, что вам не нужно реализовывать какие-либо двоичные библиотеки, такие как ImageMagick.
ChrisRich
Это точно так же, как этот ответ (владелец lwip), но позже: stackoverflow.com/a/24543924/1525495
Хорхе Фуэнтес Гонсалес,
12

Существует хорошая библиотека для работы с изображениями, полностью написанная на JavaScript, без зависимостей от других библиотек, Jimp. https://github.com/oliver-moran/jimp

Пример использования:

var Jimp = require("jimp");

// open a file called "lenna.png"
Jimp.read("lenna.png", function (err, lenna) {
    if (err) throw err;
    lenna.resize(256, 256)            // resize
         .quality(60)                 // set JPEG quality
         .write("lena-small.jpg"); // save
});
edtech
источник
Насколько это быстро по сравнению с ImageMagik?
Christopher Grigg
2
У Sharp есть набор тестов: sharp.dimens.io/en/stable/performance --- В этом тесте jimp в 5 раз медленнее, чем IM, и в 30 раз медленнее, чем Sharp. Конечно, достаточно быстро - это достаточно быстро, и скорость - не единственный фактор, который следует учитывать.
jcupitt
В реальном времени, может быть, и нет, но Jimp отлично подходит для того, чтобы просто писать файлы миниатюр нескольких размеров (и впоследствии извлекать их как кэшированные файлы).
coderofsalvation
jimp не поддерживает webp и не будет поддерживать в ближайшем будущем. См .: github.com/oliver-moran/jimp/issues/144
Вячеслав Добромыслов,
8

Sharp в последнее время пользуется некоторой популярностью, но это та же идея, что и привязки * Magick.

Однако необходимость установки ImageMagick / GraphicsMagick для простого изменения размера изображения кажется мне слишком излишней.

Изменение размера изображения совсем не простое. Формат JPEG особенно сложен, и есть несколько способов масштабирования графики с результатами разного качества, некоторые из них легко реализовать. Для этой работы существуют библиотеки обработки изображений, поэтому, если нет другой причины, по которой вы не можете их установить, сделайте это.

Ry-
источник
13
Возможно, я ленивый разработчик, но как только я увидел процесс установки ImageMagick и задумался, сколько я потрачу на его установку на свой инстанс Amazon AWS EC2, я сразу же начал искать другие варианты, особенно с учетом того, что все, что мне нужно, было возможность изменять размер изображений для миниатюр.
ChrisRich
7

Canvas в 2,3 раза быстрее ImageMagic.

Вы можете попробовать сравнить модули Node.js для обработки изображений - https://github.com/ivanoff/images-manipulation-performance

author's results:
 sharp.js : 9.501 img/sec; minFreeMem: 929Mb
 canvas.js : 8.246 img/sec; minFreeMem: 578Mb
 gm.js : 4.433 img/sec; minFreeMem: 791Mb
 gm-imagemagic.js : 3.654 img/sec; minFreeMem: 804Mb
 lwip.js : 1.203 img/sec; minFreeMem: 54Mb
 jimp.js : 0.445 img/sec; minFreeMem: 82Mb
Димитрий Иванов
источник
3

Если вам не нужно большое изображение, вы можете изменить его размер на стороне клиента перед загрузкой:

Чтение файлов в JavaScript с использованием файловых API

Изменение размера изображения на стороне клиента с помощью javascript перед загрузкой на сервер

Многие пользователи могут хорошо представить себя со смартфона, и многие из них имеют размер более 200 КБ. Обратите внимание, что данным, предоставленным клиентом, нельзя доверять, поэтому проверки на стороне сервера по-прежнему применяются.

Андрей Волгин
источник
2
Вы никогда не можете доверять клиенту, пользователю просто нужно знать конечную точку загрузки, чтобы отправлять туда все, что он хочет. Таким образом, проверки, такие как размер файла, по-прежнему применяются. Но изменение размера на стороне клиента - хорошая идея.
Кев
1

Я использовал lwip (как ранее предлагал arvind), но переключился на png- обрезку . У меня вроде работает чуть шустрее (Win 8.1 x64, Node v0.12.7). Код в репозитории выглядит невероятно легковесным и прост в использовании.

var pngcrop = require('png-crop');
var config = {left: 10, top: 100, height: 150, width: 150};
pngcrop.crop('cats.png','cats-cropped.png',config);

Конечно, он работает только с файлами png ...

Дэн Кейсли
источник
0

Sharp работает очень хорошо и прост в использовании с потоками, работает как шарм, но вам нужно скомпилировать его с версией node, это его обратная сторона. Я использовал Sharp для обработки изображений с изображением из ведра AWS S3 и работал отлично, но мне пришлось использовать другой модуль. GM не работал у меня, но Jimp работал очень хорошо!

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

Вот как я использовал Jimp в nodeJS:

const imageUrl = `SOME_URL`;
let imgExported = 'EXPORTED_PIC.png';

Jimp.read(imageUrl)
    .then(image => {
        image   
            .resize(X, Y) 
            .write(`tmp/`+ imgExported, err => { 
                if(err) 
                    console.error('Write error: ', err);
                else { ... // don't forget to put a callback() } }

            });

Также следите за порядком выполнения, поставьте обратный вызов, чтобы другие вещи не происходили, когда вы этого не хотите. Пытался использовать "await" для Jimp.read (), но это не помогло.

Алекс Сечеляну
источник
Начиная с версии 0.20, он автоматически загружает предварительно скомпилированный двоичный файл для вашей точной версии узла на большинстве платформ, поэтому нет необходимости что-либо создавать.
jcupitt
К сожалению, у меня это не сработало. Мне нужно было использовать Sharp в файловой системе только для чтения с различными версиями node.js, и мне приходилось загружать модуль sharp для каждой версии node, которую я использовал, а это занимало слишком много времени.
Alex Seceleanu
0

Вы можете сделать это с помощью jimp (node_module)

Локальная запись:

Jimp.read(path) // this can be url or local location
      .then(image=> {
          image
            .resize(size, Jimp.AUTO) // jimp.AUTO automatically sets the width so that the image doesnot looks odd
            .write('path-to-save');
      })
      .catch(err => {
        console.log(err);
      });

Для загрузки на s3 или где угодно.

Jimp.read(urls) // this can be url or local location
          .then(image=> {
              image
                .resize(size, Jimp.AUTO) // jimp.AUTO automatically sets the width so that the image doesnot looks odd
                .getBase64(Jimp.AUTO, (err, res) => {
                  const buf = new Buffer(
                    res.replace(/^data:image\/\w+;base64,/, ""),
                    "base64"
                  );
                  var data = {
                    Key: key,
                    Bucket: bucket,
                    Body: body,
                    ContentEncoding: "base64",
                    ContentType: "image/jpeg"
                  };
                  s3.putObject(data, function(err, data) {
                    if (err) {
                      throw err;
                    } else {
                      console.log("succesfully uploaded the image!");
                    }
                  });
                });
          })
          .catch(err => {
            console.log(err);
          });
Нуман Дилшад
источник
0

Мне нравится библиотека resize-img за ее простоту.

const fs = require('fs');
const resizeImg = require('resize-img');

(async () => {
    const image = fs.readFileSync('unicorn.png');

    const newImage = await resizeImg(image, { width: 128, height: 128 });

    fs.writeFileSync('unicorn-128x128.png', newImage);
})();
ns16
источник
0

Реализовано изменение размера изображения с помощью Google Drive API v3 . Этот метод рекомендуется для скрипта Google Apps для вставки изображений в Google Таблицы.

Алгоритм:

  1. Загрузите изображение в папку Google Диска.
  2. Получить общедоступный URL-адрес эскиза изображения.
  3. Замените параметр resize в URL на необходимую ширину и / или высоту. (Размер эскиза по умолчанию - 220 пикселей).
  4. Загрузите уменьшенное изображение с Google Диска.

См. Пример здесь: https://github.com/dobromyslov/google-drive-utils/blob/511c44c2c48862b47c60038423b7f71bf1d28f49/src/index.ts#L150

И остерегайтесь квот GDrive:

  • запросов в день: 1000000000
  • запросов в 100 сек на пользователя: 1000
  • запросов в 100 сек: 10000
Вячеслав Добромыслов
источник