NodeJS: Speichern eines Base64-codierten Images auf der Festplatte

162

Meine Express-App empfängt ein base64-codiertes PNG vom Browser (generiert aus Canvas mit toDataURL ()) und schreibt es in eine Datei. Die Datei ist jedoch keine gültige Bilddatei, und das Dienstprogramm "Datei" identifiziert sie einfach als "Daten".

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's not a valid image
});
Mahemoff
quelle
1
Ich habe die Antwort aktualisiert, von der ich denke, dass sie das ist, was du
Alfred
Natürlich haben Sie nicht darum gebeten, aber (in meinem Fall) mir wurde klar, dass der beste Ansatz darin bestand, die gesamte codierte Zeichenfolge in meiner Datenbank zu speichern (Sie können sie jederzeit mit laden <img src="data:image/png;base64,..." />). Nur eine Option, die andere in Betracht ziehen sollten, wenn sie diesen Thread als Referenz verwenden.
JSideris

Antworten:

324

Ich denke, Sie konvertieren die Daten etwas mehr als nötig. Sobald Sie den Puffer mit der richtigen Codierung erstellt haben, müssen Sie nur noch den Puffer in die Datei schreiben.

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

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

new Buffer (..., 'base64') konvertiert die Eingabezeichenfolge in einen Puffer, der nur ein Array von Bytes ist, indem die Eingabe als base64-codierte Zeichenfolge interpretiert wird. Dann können Sie einfach dieses Byte-Array in die Datei schreiben.

Aktualisieren

Wie in den Kommentaren erwähnt, req.rawBodyist das keine Sache mehr. Wenn Sie express/ verwenden connect, sollten Sie die bodyParser()Middleware verwenden und verwenden req.body. Wenn Sie dies mit dem Standardknoten tun, müssen Sie die eingehenden dataEreignisobjekte aggregieren Bufferund diese Bilddaten im endRückruf analysieren .

loganfsmyth
quelle
2
Außerdem enthält das Argument writeFile in Ihrem Beispiel einen leichten Tippfehler: "bufferData" -> "dataBuffer".
Mahemoff
@RJ. req.rawBodyenthält die Anforderungsdaten, die als Daten-URL codiert sind: developer.mozilla.org/en-US/docs/data_URIs . Sie müssen also den Anfangsteil entfernen, um nur die Base64-Daten zu speichern.
Loganfsmyth
2
Das ist ausgezeichnetes Zeug, danke! Für diejenigen, die dies in Zukunft finden, ist rawBody keine Eigenschaft von req mehr. Sie müssen die Express-Body-Parser-Middleware verwenden, um die Daten abzurufen.
DigitalDesignDj
10
var base64Data = req.rawBody.split (',') [1];
Anja Ishmukhametova
@notgiorgi Am besten stellen Sie eine neue Frage mit genügend Details, um Ihr Problem zu reproduzieren, und verlinken Sie auf diese Frage, dass Sie sie nicht zum Laufen bringen können.
Loganfsmyth
22

Dies ist meine vollständige Lösung, die jedes base64-Bildformat liest und im richtigen Format in der Datenbank speichert:

    // 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);
    }
Platzhalter
quelle
jemand hier, um mir zu antworten? was das betrifft??
Ich
Ich habe gerade Ihren Code geändert. fs.writeFile ("test.jpg", imageBuffer.data, function (err) {json_response ['success'] = true; res.json (json_response);}); Bild wird hochgeladen, aber das Ergebnis gefällt mir nicht so gut. Fehler: 502 Bad Gateway tatsächlich Problem in res.json, warum dies nicht gedruckt wird ...
iam
18

AKTUALISIEREN

Ich fand diesen interessanten Link, wie Sie Ihr Problem in PHP lösen können . Ich denke, Sie haben vergessen, spacedurch zu ersetzen, +wie im Link gezeigt.

Ich habe diesen Kreis von http://images-mediawiki-sites.thefullwiki.org/04/1/7/5/6204600836255205.png als Beispiel genommen, das aussieht wie:

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

Als nächstes habe ich es über http://www.greywyvern.com/code/php/binary2base64 geschrieben, was mir zurückgegeben hat:

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

hat diese Zeichenfolge gespeichert, aus base64der ich in meinem Code gelesen habe.

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's not a valid image
});

Ich bekomme einen Kreis zurück, aber das Lustige ist, dass sich die Dateigröße geändert hat :) ...

ENDE

Wenn Sie das Bild zurücklesen, müssen Sie wahrscheinlich Header einrichten

Nehmen Sie zum Beispiel imagepng von der PHP-Seite:

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

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

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

Ich denke, die zweite Zeile header('Content-Type: image/png');ist wichtig, sonst wird Ihr Bild nicht im Browser angezeigt, sondern nur eine Reihe von Binärdaten wird dem Browser angezeigt.

In Express würden Sie einfach so etwas wie unten verwenden. Ich werde Ihren Gravatar anzeigen, der sich unter http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG befindet und bei Ihnen eine JPEG-Datei ist curl --head http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG. Ich fordere nur Header an, da Curl sonst eine Reihe von Binärdaten (Google Chrome wird sofort heruntergeladen) auf der Konsole anzeigt:

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
Alfred
quelle
Danke Alfred, aber in diesem minimalen Testfall sende ich nichts vom Server zurück. Ich schreibe die Datei einfach auf die Festplatte des Servers, und es scheint, dass die Datei selbst kein gültiges Image ist. Ich bin mir ziemlich sicher, dass base64 richtig ist, aber es scheint ein Problem zu geben, es als binär zu schreiben.
Mahemoff
1
Entschuldigung, ich verstehe die Frage falsch: $. Ich werde es wieder versuchen.
Alfred
1
Vielen Dank für das Update, aber die Speicherplatzersetzung hat bei mir nicht funktioniert und war eigentlich nicht erforderlich, als ich Logans Lösung angewendet habe. Als Referenz ist die Zeichenfläche in meinem Testfall sehr einfach: var context = canvas.getContext ('2d'); context.fillStyle = "# f89"; context.fillRect (50,50,100,100);
Mahemoff
Okay, weil ich ein Bild zurückbekommen habe, als ich das getan habe, aber zumindest Ihr Problem wurde gelöst: P
Alfred
Interessant, nicht sicher, warum der toString ("binär") es in Ihrem Fall nicht durcheinander gebracht hat. In jedem Fall sollten Leerzeichen in base64 ohnehin nicht angezeigt werden, daher sollte das Ersetzen streitig sein. Es ist mit dem Beispiel, das ich sowieso zur Verfügung gestellt habe. (Ich habe eine Variante mit manuell eingefügten Zeilenumbrüchen ausprobiert. Nach dem Lesen der MIME-Spezifikation sind Zeilen mit nicht mehr als 72 Zeichen erforderlich, meistens aus Paranoia. Es stellt sich heraus, dass sie mit oder ohne Zeilenumbrüche funktionieren, solange toString ("binär"). ) wird fallen gelassen.)
Mahemoff
6

Ich musste auch Base64-codierte Bilder speichern, die Teil von Daten-URLs sind, und so habe ich ein kleines npm-Modul erstellt, um dies zu tun, falls ich (oder jemand anderes) es in Zukunft erneut tun muss. Es heißt ba64 .

Einfach ausgedrückt, nimmt es eine Daten-URL mit einem Base64-codierten Bild und speichert das Bild in Ihrem Dateisystem. Es kann synchron oder asynchron gespeichert werden. Es hat auch zwei Hilfsfunktionen, eine zum Abrufen der Dateierweiterung des Bildes und die andere zum Trennen der Base64-Codierung vom data:Schema-Präfix.

Hier ist ein Beispiel:

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
});

Installieren Sie es : npm i ba64 -S. Repo ist auf GitHub: https://github.com/HarryStevens/ba64 .

PS Später kam mir der Gedanke, dass ba64 wahrscheinlich ein schlechter Name für das Modul ist, da die Leute annehmen können, dass es Base64-Codierung und -Decodierung durchführt, was nicht der Fall ist (es gibt viele Module, die dies bereits tun). Naja.

Harry Stevens
quelle
2

Das hat es für mich einfach und perfekt gemacht.

Hervorragende Erklärung von Scott Robinson

Vom Bild zum Base64-String

let buff = fs.readFileSync('stack-abuse-logo.png');
let base64data = buff.toString('base64');

Von der Base64-Zeichenfolge zum Bild

let buff = new Buffer(data, 'base64');
fs.writeFileSync('stack-abuse-logo-out.png', buff);
dovk
quelle
1

Einfache Möglichkeit, das base64- Image in eine Datei zu konvertieren und als zufällige ID oder Namen zu speichern.

// 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
Carlos
quelle
1

Konvertieren von einer Datei mit Base64-Zeichenfolge in ein PNG-Bild.

4 Varianten, die funktionieren.

var {promisify} = require('util');
var fs = require("fs");

var readFile = promisify(fs.readFile)
var writeFile = promisify(fs.writeFile)

async function run () {

  // variant 1
  var d = await readFile('./1.txt', 'utf8')
  await writeFile("./1.png", d, 'base64')

  // variant 2
  var d = await readFile('./2.txt', 'utf8')
  var dd = new Buffer(d, 'base64')
  await writeFile("./2.png", dd)

  // variant 3
  var d = await readFile('./3.txt')
  await writeFile("./3.png", d.toString('utf8'), 'base64')

  // variant 4
  var d = await readFile('./4.txt')
  var dd = new Buffer(d.toString('utf8'), 'base64')
  await writeFile("./4.png", dd)

}

run();
Vladimir Buskin
quelle
1

Unter Funktion zum Speichern von Dateien, übergeben Sie einfach Ihre base64-Datei, es wird der Dateiname zurückgegeben, speichern Sie es in der Datenbank.

import fs from 'fs';
 const uuid = require('uuid/v1');

/*Download the base64 image in the server and returns the filename and path of image.*/
function saveImage(baseImage) {
    /*path of the folder where your project is saved. (In my case i got it from config file, root path of project).*/
    const uploadPath = "/home/documents/project";
    //path of folder where you want to save the image.
    const localPath = `${uploadPath}/uploads/images/`;
    //Find extension of file
    const ext = baseImage.substring(baseImage.indexOf("/")+1, baseImage.indexOf(";base64"));
    const fileType = baseImage.substring("data:".length,baseImage.indexOf("/"));
    //Forming regex to extract base64 data of file.
    const regex = new RegExp(`^data:${fileType}\/${ext};base64,`, 'gi');
    //Extract base64 data.
    const base64Data = baseImage.replace(regex, "");
    const filename = `${uuid()}.${ext}`;

    //Check that if directory is present or not.
    if(!fs.existsSync(`${uploadPath}/uploads/`)) {
        fs.mkdirSync(`${uploadPath}/uploads/`);
    }
    if (!fs.existsSync(localPath)) {
        fs.mkdirSync(localPath);
    }
    fs.writeFileSync(localPath+filename, base64Data, 'base64');
    return filename;
}
Shaik Matheen
quelle
1
Hat für mich gearbeitet. Und es kann für alle base64-Konvertierungen verwendet werden. Es behandelt jede Datei generisch. Danke dir!
Guilherme Sampaio
1

Sie können eine Drittanbieter-Bibliothek wie base64-img oder base64-to-image verwenden .

  1. base64-img
const base64Img = require('base64-img');

const data = 'data:image/png;base64,...';
const destpath = 'dir/to/save/image';
const filename = 'some-filename';

base64Img.img(data, destpath, filename, (err, filepath) => {}); // Asynchronous using

const filepath = base64Img.imgSync(data, destpath, filename); // Synchronous using
  1. base64-to-image
const base64ToImage = require('base64-to-image');

const base64Str = 'data:image/png;base64,...';
const path = 'dir/to/save/image/'; // Add trailing slash
const optionalObj = { fileName: 'some-filename', type: 'png' };

const { imageType, fileName } = base64ToImage(base64Str, path, optionalObj); // Only synchronous using
ns16
quelle