NodeJS: Menyimpan gambar yang disandikan base64 ke disk

162

Aplikasi My Express menerima PNG yang disandikan base64 dari browser (dihasilkan dari kanvas dengan toDataURL ()) dan menulisnya ke file. Tetapi file tersebut bukan file gambar yang valid, dan utilitas "file" hanya mengidentifikasinya sebagai "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
});
mahemoff
sumber
1
Saya memperbarui jawaban yang saya pikir adalah yang Anda butuhkan;)
Alfred
Jelas ini bukan ini yang Anda minta, tetapi (dalam kasus saya) saya menyadari bahwa pendekatan terbaik adalah hanya menyimpan seluruh string yang disandikan ke database saya (Anda selalu dapat memuatnya menggunakan <img src="data:image/png;base64,..." />). Hanya opsi untuk mempertimbangkan orang lain menggunakan utas ini sebagai referensi.
JSideris

Jawaban:

324

Saya pikir Anda mengonversi data sedikit lebih dari yang Anda butuhkan. Setelah Anda membuat buffer dengan pengkodean yang tepat, Anda hanya perlu menulis buffer ke file.

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

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

Buffer baru (..., 'base64') akan mengonversi string input menjadi Buffer, yang hanya merupakan array byte, dengan menginterpretasikan input sebagai string yang disandikan base64. Kemudian Anda bisa menulis array byte itu ke file.

Memperbarui

Seperti yang disebutkan dalam komentar, req.rawBodytidak ada lagi. Jika Anda menggunakan express/ connectmaka Anda harus menggunakan bodyParser()middleware dan gunakan req.body, dan jika Anda melakukan ini menggunakan Node standar maka Anda perlu menggabungkan objek dataacara yang masuk Bufferdan melakukan data gambar ini dengan menguraikan dalam endcallback.

loganfsmyth
sumber
2
Juga, ada sedikit kesalahan ketik dalam argumen writeFile dalam contoh Anda: "bufferData" -> "dataBuffer".
mahemoff
@RJ. req.rawBodyberisi data permintaan yang disandikan sebagai URL data: developer.mozilla.org/en-US/docs/data_URIs . Jadi, Anda harus menghapus bagian awal untuk mendapatkan hanya data base64 untuk disimpan.
loganfsmyth
2
Ini barang bagus, terima kasih! Bagi mereka yang menemukan ini di masa depan, rawBody tidak lagi menjadi properti req. Anda harus menggunakan middleware pengurai isi ulang tubuh untuk mendapatkan data.
DigitalDesignDj
10
var base64Data = req.rawBody.split (',') [1];
Anja Ishmukhametova
@ notgiorgi Terbaik untuk mengajukan pertanyaan baru dengan detail yang cukup untuk mereproduksi masalah Anda, dan tautkan ke yang ini dengan mengatakan Anda tidak bisa membuatnya berfungsi.
loganfsmyth
22

ini adalah solusi lengkap saya yang akan membaca format gambar base64 dan menyimpannya dalam format yang tepat di basis data:

    // 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);
    }
Placeholder
sumber
ada yang di sini untuk menjawab saya ?? tentang ini ??
iam
saya baru saja memodifikasi kode Anda. fs.writeFile ("test.jpg", imageBuffer.data, function (err) {json_response ['success'] = true; res.json (json_response);}); gambar diunggah tetapi hasilnya tidak seperti yang saya sukai .. kesalahan: 502 Bad Gateway sebenarnya bermasalah di res.json, mengapa ini tidak mencetak ...
iam
18

MEMPERBARUI

Saya menemukan tautan menarik ini bagaimana menyelesaikan masalah Anda di PHP . Saya pikir Anda lupa untuk mengganti spacedengan +seperti yang ditunjukkan pada tautan.

Saya mengambil lingkaran ini dari http://images-mediawiki-sites.thefullwiki.org/04/1/7/5/6204600836255205.png sebagai sampel yang terlihat seperti:

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

Selanjutnya saya memasukkannya melalui http://www.greywyvern.com/code/php/binary2base64 yang mengembalikan saya:

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

menyimpan string ini base64yang saya baca dari dalam kode saya.

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

Saya mendapatkan lingkaran kembali, tetapi lucunya file files telah berubah :)

AKHIR

Ketika Anda membaca kembali gambar, saya pikir Anda perlu mengatur header

Ambil contoh gambar dari halaman PHP:

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

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

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

Saya pikir baris kedua header('Content-Type: image/png'); , penting kalau gambar Anda tidak akan ditampilkan di browser, tetapi hanya sekelompok data biner yang ditampilkan ke browser.

Dalam Express, Anda cukup menggunakan sesuatu seperti di bawah ini. Saya akan menampilkan gravatar Anda yang terletak di http://www.gravatar.com/[/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG dan merupakan file jpeg saat Anda curl --head http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG. Saya hanya meminta tajuk karena ikal lain akan menampilkan banyak hal biner (Google Chrome segera mengunduh) ke konsol:

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
sumber
Terima kasih Alfred, tetapi dalam kasus tes minimal ini, saya tidak mengirim apa pun kembali dari server. Saya hanya menulis file ke disk di server, dan tampaknya file itu sendiri bukan gambar yang valid. Saya cukup yakin bahwa base64 benar, tetapi tampaknya ada masalah menuliskannya sebagai biner.
mahemoff
1
Maaf saya salah paham pertanyaan: $. Saya akan mencoba lagi.
Alfred
1
Terima kasih atas pembaruannya, tetapi penggantian ruang tidak berfungsi untuk saya, dan sebenarnya tidak diperlukan ketika saya menerapkan solusi Logan. Untuk referensi, kanvas sangat sederhana dalam kasus pengujian saya: var context = canvas.getContext ('2d'); context.fillStyle = "# f89"; context.fillRect (50,50.100.100);
mahemoff
Oke karena saya mendapatkan gambar kembali ketika saya melakukan ini, tetapi setidaknya masalah Anda telah diselesaikan: P
Alfred
Menarik, tidak yakin mengapa toString ("biner") tidak mengacaukannya dalam kasus Anda. Bagaimanapun, spasi seharusnya tidak muncul secara alami di base64, jadi ganti harus diperdebatkan. Ini dengan contoh yang saya berikan. (Saya memang mencoba varian dengan baris baru yang disisipkan secara manual, setelah membaca spesifikasi MIME membutuhkan garis tidak lebih dari 72 karakter, sebagian besar dari paranoia ... ternyata berfungsi dengan atau tanpa baris baru, selama toString ("binary" ) dijatuhkan.)
mahemoff
6

Saya juga harus menyimpan gambar yang disandikan Base64 yang merupakan bagian dari URL data, jadi saya akhirnya membuat modul npm kecil untuk melakukannya jika saya (atau orang lain) perlu melakukannya lagi di masa depan. Ini disebut ba64 .

Sederhananya, dibutuhkan URL data dengan gambar yang disandikan Base64 dan menyimpan gambar ke sistem file Anda. Itu dapat menyimpan secara sinkron atau asinkron. Ini juga memiliki dua fungsi pembantu, satu untuk mendapatkan ekstensi file gambar, dan yang lainnya untuk memisahkan pengkodean Base64 dari data:awalan skema.

Ini sebuah contoh:

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

Menginstalnya: npm i ba64 -S. Repo ada di GitHub: https://github.com/HarryStevens/ba64 .

PS Terpikir oleh saya kemudian bahwa ba64 mungkin adalah nama yang buruk untuk modul karena orang mungkin menganggap itu melakukan encoding dan decoding Base64, yang tidak (ada banyak modul yang sudah melakukan itu). Baiklah.

Harry Stevens
sumber
2

Ini melakukannya untuk saya dengan sederhana dan sempurna.

Penjelasan luar biasa dari Scott Robinson

Dari gambar ke string base64

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

Dari string base64 ke gambar

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

Cara mudah untuk mengonversi gambar base64 ke file dan menyimpan beberapa id atau nama acak.

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

Konversi dari file dengan string base64 ke gambar png.

4 varian yang berfungsi.

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
sumber
1

Di bawah ini berfungsi untuk menyimpan file, cukup kirimkan file base64 Anda, itu mengembalikan nama file simpan dalam DB.

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
sumber
1
Bekerja untukku. Dan itu dapat digunakan untuk konversi base64 apa pun. Ini memperlakukan setiap file secara umum. Terima kasih!
Guilherme Sampaio
1

Anda bisa menggunakan perpustakaan pihak ketiga seperti base64-img atau 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-ke-gambar
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
sumber