NodeJS: Salvando uma imagem codificada em base64 no disco

Meu aplicativo Express está recebendo um PNG codificado em base64 do navegador (gerado a partir da canvas com toDataURL ()) e gravando-o em um arquivo. Mas o arquivo não é um arquivo de imagem válido e o utilitário “arquivo” simplesmente o identifica como “dados”.

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

Eu acho que você está convertendo os dados um pouco mais do que o necessário. Depois de criar o buffer com a codificação correta, basta gravar o buffer no arquivo.

 var base64Data = req.rawBody.replace(/^data:image\/png;base64,/, ""); require("fs").writeFile("out.png", base64Data, 'base64', function(err) { console.log(err); }); 

O novo Buffer (…, ‘base64’) converterá a string de input para um Buffer, que é apenas uma matriz de bytes, interpretando a input como uma string codificada na base64. Então você pode simplesmente escrever essa matriz de bytes no arquivo.

Atualizar

Como mencionado nos comentários, req.rawBody não é mais uma coisa. Se você estiver usando express / connect então você deve usar o middleware bodyParser() e usar req.body , e se você estiver fazendo isso usando o Nó padrão, então você precisa agregar os objects Buffer events de data input e fazer esta análise de dados de imagem no end callback.

Esta é a minha solução completa que iria ler qualquer formato de imagem base64 e salvá-lo no formato adequado no database:

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

ATUALIZAR

Eu encontrei este link interessante como resolver seu problema no PHP . Acho que você esqueceu de replace o space por + conforme mostrado no link.

Eu peguei este círculo de http://images-mediawiki-sites.thefullwiki.org/04/1/7/5/6204600836255205.png como exemplo que se parece com:

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

Em seguida, coloquei em http://www.greywyvern.com/code/php/binary2base64, que me retornou:

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

salvei esta string para base64 que eu li no meu código.

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

Eu recebo um círculo de volta, mas o engraçado é que o tamanho do arquivo mudou:) …

FIM

Quando você lê de volta a imagem, eu acho que você precisa configurar os headers

Tomemos por exemplo o imagepng da página do PHP:

 < ?php $im = imagecreatefrompng("test.png"); header('Content-Type: image/png'); imagepng($im); imagedestroy($im); ?> 

Eu acho que o header('Content-Type: image/png'); da segunda linha header('Content-Type: image/png'); , é importante que sua imagem não seja exibida no navegador, mas apenas um monte de dados binários é mostrado para o navegador.

No Express você simplesmente usaria algo como abaixo. Eu vou mostrar o seu gravatar que está localizado em http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG e é um arquivo jpeg quando você curl --head http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG . Eu só peço headers porque else curl irá mostrar um monte de coisas binárias (o Google Chrome vai imediatamente baixar) para consolar:

 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 

Eu também tive que salvar imagens codificadas em Base64 que fazem parte de URLs de dados, então acabei fazendo um pequeno módulo npm para fazer isso no caso de eu (ou outra pessoa) precisar fazer isso novamente no futuro. Chama-se ba64 .

Simplificando, ele usa uma URL de dados com uma imagem codificada em Base64 e salva a imagem em seu sistema de arquivos. Pode salvar de forma síncrona ou assíncrona. Ele também tem duas funções auxiliares, uma para obter a extensão de arquivo da imagem e outra para separar a codificação Base64 do prefixo data: scheme.

Aqui está um exemplo:

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

Instale-o: npm i ba64 -S . O Repo está no GitHub: https://github.com/HarryStevens/ba64 .

PS Ocorreu-me mais tarde que o ba64 é provavelmente um mau nome para o módulo, pois as pessoas podem assumir que ele faz codificação e decodificação Base64, o que não acontece (há muitos módulos que já fazem isso). Ah bem.

Maneira fácil de converter a imagem base64 em arquivo e salvar como um id ou nome random.

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

Convertendo de arquivo com string base64 para imagem png.

4 variantes que funcionam.

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