NodeJS: enregistrement d'une image encodée en base64 sur le disque


162

Mon application Express reçoit un PNG encodé en base64 du navigateur (généré à partir du canevas avec toDataURL ()) et l'écrit dans un fichier. Mais le fichier n'est pas un fichier image valide, et l'utilitaire "fichier" l'identifie simplement comme "données".

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

1
J'ai mis à jour la réponse qui, je pense, est ce dont vous aviez besoin en premier lieu;)
Alfred

Évidemment, ce n'est pas ce que vous avez demandé, mais (dans mon cas), j'ai réalisé que la meilleure approche était simplement de stocker toute la chaîne codée dans ma base de données (vous pouvez toujours la charger en utilisant <img src="data:image/png;base64,..." />). Juste une option à considérer pour les autres utilisant ce fil de discussion comme référence.
JSideris

Réponses:


325

Je pense que vous convertissez les données un peu plus que nécessaire. Une fois que vous avez créé le tampon avec le codage approprié, il vous suffit d'écrire le tampon dans le fichier.

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

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

new Buffer (..., 'base64') convertira la chaîne d'entrée en Buffer, qui n'est qu'un tableau d'octets, en interprétant l'entrée comme une chaîne encodée en base64. Ensuite, vous pouvez simplement écrire ce tableau d'octets dans le fichier.

Mettre à jour

Comme mentionné dans les commentaires, ce req.rawBodyn'est plus une chose. Si vous utilisez express/, connectvous devez utiliser le bodyParser()middleware et utiliser req.body, et si vous le faites à l'aide de Node standard, vous devez regrouper les objets d' dataévénements entrants Bufferet effectuer cette analyse des données d'image dans le endrappel.


2
De plus, il y a une légère faute de frappe dans l'argument writeFile dans votre exemple: "bufferData" -> "dataBuffer".
mahemoff

@RJ. req.rawBodycontient les données de la requête qui sont encodées en tant qu'URL de données: developer.mozilla.org/en-US/docs/data_URIs . Vous devez donc supprimer la partie de début pour obtenir uniquement les données base64 à enregistrer.
loganfsmyth

2
Ce sont d'excellents trucs, merci! Pour ceux qui trouveront cela dans le futur, rawBody n'est plus une propriété de req. Vous devez utiliser le middleware de l'analyseur de corps express pour obtenir les données.
DigitalDesignDj

10
var base64Data = req.rawBody.split (',') [1];
Anja Ishmukhametova

@notgiorgi Il est préférable de poser une nouvelle question avec suffisamment de détails pour reproduire votre problème, et un lien vers celle-ci indiquant que vous ne pouvez pas la faire fonctionner.
loganfsmyth

22

c'est ma solution complète qui lit n'importe quel format d'image base64 et l'enregistre au format approprié dans la base de données:

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

quelqu'un ici pour me répondre ?? à ce sujet ??
iam

je viens de modifier votre code. fs.writeFile ("test.jpg", imageBuffer.data, function (err) {json_response ['success'] = true; res.json (json_response);}); image est téléchargée mais le résultat est pas à me aimer .. Erreur: 502 Passerelle réellement problème dans res.json, pourquoi cela ne l' impression ...
iam

18

METTRE À JOUR

J'ai trouvé ce lien intéressant pour résoudre votre problème en PHP . Je pense que vous avez oublié de remplacer spacepar +comme indiqué dans le lien.

J'ai pris ce cercle de http://images-mediawiki-sites.thefullwiki.org/04/1/7/5/6204600836255205.png comme exemple qui ressemble à:

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

Ensuite, je l'ai mis sur http://www.greywyvern.com/code/php/binary2base64 qui m'a renvoyé:

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

a enregistré cette chaîne dans base64laquelle j'ai lu dans mon code.

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

Je récupère un cercle, mais le plus drôle est que la taille du fichier a changé :) ...

FIN

Lorsque vous relisez l'image, je pense que vous devez configurer les en-têtes

Prenez par exemple imagepng de la page PHP:

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

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

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

Je pense que la deuxième ligne header('Content-Type: image/png');est importante sinon votre image ne sera pas affichée dans le navigateur, mais juste un tas de données binaires est montré au navigateur.

Dans Express, vous utiliseriez simplement quelque chose comme ci-dessous. Je vais afficher votre gravatar qui se trouve à http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG et est un fichier jpeg lorsque vous curl --head http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG. Je ne demande que des en-têtes car sinon curl affichera un tas de trucs binaires (Google Chrome va immédiatement télécharger) sur la console:

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

Merci Alfred, mais dans ce cas de test minimal, je ne renvoie rien du serveur. J'écris simplement le fichier sur le disque sur le serveur, et il semble que le fichier lui-même ne soit pas une image valide. Je suis assez certain que la base64 est correcte, mais il semble y avoir un problème pour l'écrire en binaire.
mahemoff

1
Désolé, j'ai mal compris la question: $. Je vais essayer de nouveau.
Alfred

1
Merci pour la mise à jour, mais la substitution d'espace n'a pas fonctionné pour moi, et n'était en fait pas nécessaire lorsque j'ai appliqué la solution de Logan. Pour référence, le canevas est très simple dans mon cas de test: var context = canvas.getContext ('2d'); context.fillStyle = "# f89"; context.fillRect (50, 50, 100, 100);
mahemoff

D'accord parce que j'ai eu une image quand j'ai fait ça, mais au moins votre problème a été résolu: P
Alfred

Intéressant, je ne sais pas pourquoi le toString ("binaire") ne l'a pas gâché dans votre cas. Dans tous les cas, les espaces ne devraient pas apparaître naturellement dans base64 de toute façon, donc le remplacement devrait être sans objet. C'est avec l'exemple que j'ai fourni de toute façon. (J'ai essayé une variante avec des nouvelles lignes insérées manuellement, après avoir lu la spécification MIME, il faut des lignes ne dépassant pas 72 caractères, principalement par paranoïa ... il s'avère que cela fonctionne avec ou sans les nouvelles lignes, tant que toString ("binaire") ) est abandonné.)
mahemoff

6

J'ai également dû enregistrer des images encodées en Base64 qui font partie des URL de données, alors j'ai fini par créer un petit module npm pour le faire au cas où je (ou quelqu'un d'autre) aurait besoin de le refaire à l'avenir. Il s'appelle ba64 .

En termes simples, il prend une URL de données avec une image encodée en Base64 et enregistre l'image dans votre système de fichiers. Il peut enregistrer de manière synchrone ou asynchrone. Il dispose également de deux fonctions d'assistance, l'une pour obtenir l'extension de fichier de l'image et l'autre pour séparer l'encodage Base64 du data:préfixe du schéma.

Voici un exemple:

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

Installez - le: npm i ba64 -S. Repo est sur GitHub: https://github.com/HarryStevens/ba64 .

PS Il m'est apparu plus tard que ba64 est probablement un mauvais nom pour le module car les gens peuvent supposer qu'il fait l'encodage et le décodage Base64, ce qui n'est pas le cas (il y a beaucoup de modules qui le font déjà). Tant pis.


2

Cela l'a fait pour moi simplement et parfaitement.

Excellente explication par Scott Robinson

De l'image à la chaîne base64

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

De la chaîne base64 à l'image

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

1

Un moyen facile de convertir une image base64 en fichier et de l'enregistrer sous un identifiant ou un nom aléatoire.

// 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

1

Conversion d'un fichier avec une chaîne base64 en image png.

4 variantes qui fonctionnent.

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

1

Ci-dessous la fonction pour enregistrer les fichiers, passez simplement votre fichier base64, il renvoie le nom de fichier, enregistrez-le dans la base de données.

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

1
A travaillé pour moi. Et il peut être utilisé pour toutes les conversions en base64. Il traite chaque fichier de manière générique. Je vous remercie!
Guilherme Sampaio

1

Vous pouvez utiliser une bibliothèque tierce telle que base64-img ou base64-to-image .

  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 vers 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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.