231 votes

Comment puis-je enregistrer une image codée en base64 sur le disque ?

Mon application Express reçoit un PNG codé 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 "file" l'identifie simplement comme "data".

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 votes

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

0 votes

É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 la chaîne encodée entière dans ma base de données (vous pouvez toujours la charger en utilisant la commande <img src="data:image/png;base64,..." /> ). C'est juste une option à prendre en compte pour les autres personnes qui utilisent ce fil de discussion comme référence.

424voto

loganfsmyth Points 25483

Je pense que vous convertissez les données un peu plus que nécessaire. Une fois que vous avez créé le tampon avec le bon encodage, 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 un Buffer, qui est juste 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.

Mise à jour

Comme mentionné dans les commentaires, req.rawBody n'existe plus. Si vous utilisez express / connect alors vous devez utiliser le bodyParser() et utiliser req.body et si vous utilisez le Node standard, vous devez agréger les données entrantes. data événement Buffer et effectuer l'analyse des données de l'image dans l'objet end le rappel.

2 votes

En outre, il y a une légère erreur de frappe dans l'argument writeFile de votre exemple : "bufferData" -> "dataBuffer".

0 votes

@RJ. req.rawBody contient les données de la demande qui sont codées comme une URL de données : developer.mozilla.org/en-US/docs/data_URIs . Il faut donc enlever le début pour n'obtenir que les données en base64 à sauvegarder.

2 votes

C'est un excellent travail, merci ! Pour ceux qui trouveront ceci dans le futur, rawBody n'est plus une propriété de req. Vous devez utiliser le middleware express body parser pour obtenir les données.

34voto

Hellen Points 196

Voici ma solution complète qui lirait n'importe quel format d'image base64 et l'enregistrerait dans le 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);
    }

0 votes

Quelqu'un peut-il me répondre à ce sujet ?

0 votes

Je viens de modifier votre code. fs.writeFile("test.jpg", imageBuffer.data, function(err ) { json_response['success'] = true ; res.json(json_response) ; }) ; l'image est téléchargée mais le résultat ne me plaît pas... error : 502 Bad Gateway actually problem in res.json , why this is not print...

0 votes

Cette réponse est une bouée de sauvetage !

30voto

dovk Points 58

C'est ce que j'ai fait, simplement et parfaitement.

Excellente explication de Scott Robinson

De l'image à la chaîne de caractères 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 votes

Mis à jour la réponse en remplaçant new par .from, pour supprimer l'avertissement de sécurité

20voto

Alfred Points 32190

UPDATE

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

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

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

Ensuite, je l'ai mis dans 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 base64 que je lis 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 reçois un cercle de retour, mais le plus drôle est que la taille des fichiers a changé :)...

FIN

Quand 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 important, sinon votre image ne sera pas affichée dans le navigateur, mais juste un tas de données binaires est montré au navigateur.

Sur Express vous devez simplement utiliser quelque chose comme ci-dessous. Je vais afficher votre gravatar qui se trouve à l'adresse suivante 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 les en-têtes parce que sinon curl va afficher un tas de trucs binaires (Google Chrome va immédiatement télécharger) à 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

0 votes

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

1 votes

Désolé d'avoir mal compris la question :$. Je vais réessayer.

1 votes

Merci pour la mise à jour, mais la substitution par des espaces 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) ;

7voto

Harry Stevens Points 429

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

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

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 .

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X