NodeJS: сохранение образа с кодировкой base64 на диск

Приложение My Express получает PNG с кодировкой base64 из браузера (сгенерировано из canvas с toDataURL()) и записывает его в файл. Но файл не является допустимым файлом изображения, а утилита "file" просто идентифицирует его как "данные".

var body = req.rawBody,
  base64Data = body.replace(/^data:image\/png;base64,/,""),
  binaryData = new Buffer(base64Data, 'base64').toString('binary');

require("fs").writeFile("out.png", binaryData, "binary", function(err) {
  console.log(err); // writes out file without error, but it not a valid image
});

Ответ 1

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

var base64Data = req.rawBody.replace(/^data:image\/png;base64,/, "");

require("fs").writeFile("out.png", base64Data, 'base64', function(err) {
  console.log(err);
});

new Buffer (..., 'base64') преобразует входную строку в буфер, который является всего лишь массивом байтов, интерпретируя ввод как строку с кодировкой base64. Затем вы можете просто записать этот массив байтов в файл.

Update

Как уже упоминалось в комментариях, req.rawBody уже не вещь. Если вы используете express/connect, тогда вы должны использовать промежуточное программное обеспечение bodyParser() и использовать req.body, и если вы делаете это с помощью стандартного Node, вам необходимо агрегировать входящее событие data Buffer и выполните этот анализ данных изображения в обратном вызове end.

Ответ 2

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

    // Save base64 image to disk
    try
    {
        // Decoding base-64 image
        // Source: http://stackoverflow.com/questions/20267939/nodejs-write-base64-image-file
        function decodeBase64Image(dataString) 
        {
          var matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
          var response = {};

          if (matches.length !== 3) 
          {
            return new Error('Invalid input string');
          }

          response.type = matches[1];
          response.data = new Buffer(matches[2], 'base64');

          return response;
        }

        // Regular expression for image type:
        // This regular image extracts the "jpeg" from "image/jpeg"
        var imageTypeRegularExpression      = /\/(.*?)$/;      

        // Generate random string
        var crypto                          = require('crypto');
        var seed                            = crypto.randomBytes(20);
        var uniqueSHA1String                = crypto
                                               .createHash('sha1')
                                                .update(seed)
                                                 .digest('hex');

        var base64Data = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAZABkAAD/4Q3zaHR0cDovL25zLmFkb2JlLmN...';

        var imageBuffer                      = decodeBase64Image(base64Data);
        var userUploadedFeedMessagesLocation = '../img/upload/feed/';

        var uniqueRandomImageName            = 'image-' + uniqueSHA1String;
        // This variable is actually an array which has 5 values,
        // The [1] value is the real image extension
        var imageTypeDetected                = imageBuffer
                                                .type
                                                 .match(imageTypeRegularExpression);

        var userUploadedImagePath            = userUploadedFeedMessagesLocation + 
                                               uniqueRandomImageName +
                                               '.' + 
                                               imageTypeDetected[1];

        // Save decoded binary image to disk
        try
        {
        require('fs').writeFile(userUploadedImagePath, imageBuffer.data,  
                                function() 
                                {
                                  console.log('DEBUG - feed:message: Saved to disk image attached by user:', userUploadedImagePath);
                                });
        }
        catch(error)
        {
            console.log('ERROR:', error);
        }

    }
    catch(error)
    {
        console.log('ERROR:', error);
    }

Ответ 3

UPDATE

Я нашел эту интересную ссылку, как решить вашу проблему в PHP. Я думаю, вы забыли заменить space на +, как показано в ссылке.

Я взял этот круг из http://images-mediawiki-sites.thefullwiki.org/04/1/7/5/6204600836255205.png в качестве образца, который выглядит следующим образом:

http://images-mediawiki-sites.thefullwiki.org/04/1/7/5/6204600836255205.png

Затем я передал его http://www.greywyvern.com/code/php/binary2base64, который вернул мне:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAAAAACPAi4CAAAAB3RJTUUH1QEHDxEhOnxCRgAAAAlwSFlzAAAK8AAACvABQqw0mAAAAXBJREFUeNrtV0FywzAIxJ3+K/pZyctKXqamji0htEik9qEHc3JkWC2LRPCS6Zh9HIy/AP4FwKf75iHEr6eU6Mt1WzIOFjFL7IFkYBx3zWBVkkeXAUCXwl1tvz2qdBLfJrzK7ixNUmVdTIAB8PMtxHgAsFNNkoExRKA+HocriOQAiC+1kShhACwSRGAEwPP96zYIoE8Pmph9qEWWKcCWRAfA/mkfJ0F6dSoA8KW3CRhn3ZHcW2is9VOsAgoqHblncAsyaCgcbqpUZQnWoGTcp/AnuwCoOUjhIvCvN59UBeoPZ/AYyLm3cWVAjxhpqREVaP0974iVwH51d4AVNaSC8TRNNYDQEFdlzDW9ob10YlvGQm0mQ+elSpcCCBtDgQD7cDFojdx7NIeHJkqi96cOGNkfZOroZsHtlPYoR7TOp3Vmfa5+49uoSSRyjfvc0A1kLx4KC6sNSeDieD1AWhrJLe0y+uy7b9GjP83l+m68AJ72AwSRPN5g7uwUAAAAAElFTkSuQmCC

сохранил эту строку в base64, которую я прочитал в своем коде.

var fs      = require('fs'),
data        = fs.readFileSync('base64', 'utf8'),
base64Data,
binaryData;

base64Data  =   data.replace(/^data:image\/png;base64,/, "");
base64Data  +=  base64Data.replace('+', ' ');
binaryData  =   new Buffer(base64Data, 'base64').toString('binary');

fs.writeFile("out.png", binaryData, "binary", function (err) {
    console.log(err); // writes out file without error, but it not a valid image
});

Я получаю круг назад, но самое смешное в том, что размер файла изменился:)...

END

Когда вы читаете обратное изображение, я думаю, вам нужно настроить заголовки

Возьмем, например, imagepng на странице PHP:

<?php
$im = imagecreatefrompng("test.png");

header('Content-Type: image/png');

imagepng($im);
imagedestroy($im);
?>

Я думаю, что вторая строка header('Content-Type: image/png'); важна, иначе ваше изображение не будет отображаться в браузере, но в браузере отображается только куча двоичных данных.

В Express вы просто просто используете что-то вроде ниже. Я собираюсь отобразить ваш gravatar, который находится в http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG и является файлом jpeg, когда вы curl --head http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG. Я только запрашиваю заголовки, потому что иначе curl отобразит кучу бинарных файлов (Google Chrome сразу же загружается) на консоль:

curl --head "http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG"
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 03 Aug 2011 12:11:25 GMT
Content-Type: image/jpeg
Connection: keep-alive
Last-Modified: Mon, 04 Oct 2010 11:54:22 GMT
Content-Disposition: inline; filename="cabf735ce7b8b4471ef46ea54f71832d.jpeg"
Access-Control-Allow-Origin: *
Content-Length: 1258
X-Varnish: 2356636561 2352219240
Via: 1.1 varnish
Expires: Wed, 03 Aug 2011 12:16:25 GMT
Cache-Control: max-age=300
Source-Age: 1482

$ mkdir -p ~/tmp/6922728
$ cd ~/tmp/6922728/
$ touch app.js

app.js

var app = require('express').createServer();

app.get('/', function (req, res) {
    res.contentType('image/jpeg');
    res.sendfile('cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG');
});

app.get('/binary', function (req, res) {
    res.sendfile('cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG');
});

app.listen(3000);

$ wget "http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG"
$ node app.js

Ответ 4

Легкий способ преобразования изображения base64 в файл и сохранения в виде некоторого случайного идентификатора или имени.

// to create some random id or name for your image name
const imgname = new Date().getTime().toString();

// to declare some path to store your converted image
const path = yourpath.png    

// image takes from body which you uploaded
const imgdata = req.body.image;    

// to convert base64 format into random filename
const base64Data = imgdata.replace(/^data:([A-Za-z-+/]+);base64,/, '');
fs.writeFile(path, base64Data, 'base64', (err) => {
    console.log(err);
});

// assigning converted image into your database
req.body.coverImage = imgname

Ответ 5

Мне также пришлось сохранять изображения с кодировкой Base64, которые являются частью URL-адресов данных, поэтому я сделал небольшой модуль npm, чтобы сделать это, если мне (или кому-то еще) понадобится сделать это снова в будущем. Он назывался ba64.

Проще говоря, он берет URL-адрес данных с кодировкой Base64 и сохраняет изображение в вашей файловой системе. Он может сохранять синхронно или асинхронно. Он также имеет две вспомогательные функции: один для получения расширения файла изображения, а другой - для разделения кодировки Base64 на префикс схемы data:.

Вот пример:

var ba64 = require("ba64"),
    data_url = "data:image/jpeg;base64,[Base64 encoded image goes here]";

// Save the image synchronously.
ba64.writeImageSync("myimage", data_url); // Saves myimage.jpeg.

// Or save the image asynchronously.
ba64.writeImage("myimage", data_url, function(err){
    if (err) throw err;

    console.log("Image saved successfully");

    // do stuff
});

Установите его: npm i ba64 -S. Репо находится на GitHub: https://github.com/HarryStevens/ba64.

P.S. Позднее мне пришло в голову, что ba64, вероятно, является плохим именем для модуля, так как люди могут предположить, что он кодирует и декодирует Base64, чего нет (есть много модулей, которые уже делают это). О, хорошо.