NodeJS: การบันทึกอิมเมจที่เข้ารหัส base64 ลงในดิสก์


162

แอป My Express กำลังรับ PNG ที่เข้ารหัส 64 จากเบราว์เซอร์ (สร้างจาก Canvas ด้วย toDataURL ()) และเขียนลงในไฟล์ แต่ไฟล์ไม่ใช่ไฟล์ภาพที่ถูกต้องและยูทิลิตี "file" นั้นระบุว่าเป็น "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
ฉันอัปเดตคำตอบซึ่งฉันคิดว่าเป็นสิ่งที่คุณต้องการในตอนแรก;)
อัลเฟรด

เห็นได้ชัดว่านี่ไม่ใช่สิ่งที่คุณขอ แต่ (ในกรณีของฉัน) ฉันรู้ว่าวิธีที่ดีที่สุดคือเพียงเก็บสตริงที่เข้ารหัสทั้งหมดไว้ในฐานข้อมูลของฉัน (คุณสามารถโหลดได้เสมอ<img src="data:image/png;base64,..." />) เป็นเพียงตัวเลือกในการพิจารณาให้ผู้อื่นที่ใช้เธรดนี้เป็นข้อมูลอ้างอิง
JSideris

คำตอบ:


324

ฉันคิดว่าคุณกำลังแปลงข้อมูลมากกว่าที่คุณต้องการ เมื่อคุณสร้างบัฟเฟอร์ด้วยการเข้ารหัสที่เหมาะสมคุณเพียงแค่ต้องเขียนบัฟเฟอร์ไปยังไฟล์

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

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

new Buffer (... , 'base64') จะแปลงสตริงอินพุตเป็น Buffer ซึ่งเป็นเพียงอาร์เรย์ไบต์โดยตีความอินพุตเป็นสตริงที่เข้ารหัส base64 จากนั้นคุณสามารถเขียนอาร์เรย์ไบต์นั้นลงในไฟล์

ปรับปรุง

ดังที่ได้กล่าวไว้ในความคิดเห็นreq.rawBodyไม่มีสิ่งใด หากคุณกำลังใช้express/ connectดังนั้นคุณควรใช้bodyParser()มิดเดิลแวร์และใช้req.bodyและหากคุณกำลังทำสิ่งนี้โดยใช้โหนดมาตรฐานคุณจะต้องรวมวัตถุdataเหตุการณ์ที่เข้ามาBufferและทำการแยกวิเคราะห์ข้อมูลภาพในการendเรียกกลับ


2
นอกจากนี้ยังมีความผิดพลาดเล็กน้อยในอาร์กิวเมนต์ writeFile ในตัวอย่างของคุณ: "bufferData" -> "dataBuffer"
mahemoff

@RJ req.rawBodyมีข้อมูลการร้องขอที่จะถูกเข้ารหัสเป็น URL ของข้อมูล: developer.mozilla.org/en-US/docs/data_URIs ดังนั้นคุณต้องถอดส่วนเริ่มต้นออกเพื่อรับเพียงข้อมูล base64 เพื่อบันทึก
loganfsmyth

2
นี่คือสิ่งที่ยอดเยี่ยมขอบคุณ! สำหรับผู้ที่พบสิ่งนี้ในอนาคต rawBody จะไม่ใช่สมบัติของ req อีกต่อไป คุณต้องใช้มิดเดิลแวร์ตัวแยกวิเคราะห์ด่วนเพื่อรับข้อมูล
DigitalDesignDj

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

@notgiorgi ที่ดีที่สุดที่จะถามคำถามใหม่ที่มีรายละเอียดเพียงพอที่จะทำให้เกิดปัญหาของคุณอีกครั้งและเชื่อมโยงไปยังสิ่งนี้โดยบอกว่าคุณไม่สามารถทำงานได้
loganfsmyth

22

นี่เป็นโซลูชันที่สมบูรณ์ของฉันซึ่งจะอ่านรูปแบบภาพ base64 ใด ๆ และบันทึกในรูปแบบที่เหมาะสมในฐานข้อมูล:

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

คนใดคนหนึ่งที่นี่เพื่อตอบฉัน เกี่ยวกับเรื่องนี้??
เอียน

ฉันเพิ่งแก้ไขรหัสของคุณ fs.writeFile ("test.jpg", imageBuffer.data, ฟังก์ชั่น (err) {json_response ['สำเร็จ'] = จริง; res.json (json_response);}); อัปโหลดภาพแล้ว แต่ผลลัพธ์ไม่ได้เป็นที่ชื่นชอบของฉัน .. ข้อผิดพลาด: 502 Bad Gateway มีปัญหาจริง ๆ ใน res.json เหตุใดจึงไม่พิมพ์ ...
iam

18

UPDATE

ฉันพบการเชื่อมโยงที่น่าสนใจถึงวิธีการแก้ปัญหาของคุณได้ใน PHP ฉันคิดว่าคุณลืมแทนที่spaceด้วย+ตามที่แสดงในลิงค์

ฉันใช้แวดวงนี้จากhttp://images-mediawiki-sites.thefullwiki.org/04/1/7/5/6204600836255205.pngเป็นตัวอย่างซึ่งดูเหมือนว่า:

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

ต่อไปฉันใส่มันผ่านhttp://www.greywyvern.com/code/php/binary2base64ซึ่งส่งคืนฉัน:

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

บันทึกสตริงนี้base64ที่ฉันอ่านจากในรหัสของฉัน

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

ฉันได้วงกลมกลับมา แต่สิ่งที่ตลกคือขนาดไฟล์เปลี่ยนไป:) ...

END

เมื่อคุณอ่านภาพกลับฉันคิดว่าคุณต้องตั้งค่าส่วนหัว

ยกตัวอย่างimagepngจากหน้า PHP:

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

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

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

ฉันคิดว่าบรรทัดที่สองheader('Content-Type: image/png');เป็นสิ่งสำคัญภาพของคุณจะไม่ปรากฏในเบราว์เซอร์ แต่มีเพียงข้อมูลไบนารีจำนวนมากเท่านั้นที่ปรากฏในเบราว์เซอร์

ในExpressคุณเพียงแค่ใช้สิ่งที่ต้องการด้านล่าง ฉันจะแสดง Gravatar ของคุณซึ่งตั้งอยู่ที่http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG และเป็นไฟล์ JPEG curl --head http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PGเมื่อคุณ ฉันขอส่วนหัวเท่านั้นเพราะมิฉะนั้น curl จะแสดงไฟล์ไบนารีจำนวนมาก (Google Chrome จะดาวน์โหลดทันที) เพื่อคอนโซล:

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 แต่ในกรณีทดสอบขั้นต่ำนี้ฉันไม่ได้ส่งอะไรกลับมาจากเซิร์ฟเวอร์ ฉันแค่เขียนไฟล์ไปยังดิสก์บนเซิร์ฟเวอร์และดูเหมือนว่าไฟล์นั้นไม่ใช่ภาพที่ถูกต้อง ฉันค่อนข้างแน่ใจว่า base64 ถูกต้อง แต่ดูเหมือนจะมีปัญหาในการเขียนออกมาเป็นไบนารี
mahemoff

1
ขออภัยฉันเข้าใจผิดคำถาม: $ ฉันจะลองอีกครั้ง
อัลเฟรด

1
ขอบคุณสำหรับการอัปเดต แต่การแทนที่พื้นที่ไม่ได้ผลสำหรับฉันและจริง ๆ แล้วไม่จำเป็นเมื่อฉันใช้โซลูชันของ Logan สำหรับการอ้างอิงผืนผ้าใบเรียบง่ายมากในกรณีทดสอบของฉัน: var context = canvas.getContext ('2d'); context.fillStyle = "# f89"; context.fillRect (50,50,100,100);
mahemoff

โอเคเพราะฉันได้รับภาพกลับมาเมื่อฉันทำสิ่งนี้ แต่อย่างน้อยปัญหาของคุณได้รับการแก้ไข: P
อัลเฟรด

น่าสนใจไม่แน่ใจว่าทำไม toString ("ไบนารี") จึงไม่ทำให้มันยุ่งเหยิงในกรณีของคุณ ไม่ว่าในกรณีใดช่องว่างไม่ควรปรากฏใน base64 ตามธรรมชาติดังนั้นการแทนที่ควรเป็นที่สงสัย มันเป็นตัวอย่างที่ฉันได้เตรียมไว้แล้ว (ฉันลองใช้ชุดอักขระที่ขึ้นบรรทัดใหม่ที่แทรกด้วยตนเองหลังจากอ่านข้อมูลจำเพาะ MIME ต้องมีบรรทัดไม่เกิน 72 ตัวอักษรส่วนใหญ่มาจากความหวาดระแวง ... มันกลับกลายเป็นว่ามีหรือไม่ขึ้นบรรทัดใหม่ตราบใดที่ toString ("ไบนารี" ) ถูกทิ้ง)
mahemoff

6

ฉันต้องบันทึกภาพที่เข้ารหัส Base64 ซึ่งเป็นส่วนหนึ่งของ URL ข้อมูลด้วยดังนั้นฉันจึงตัดสินใจทำโมดูล npm ขนาดเล็กเพื่อทำในกรณีที่ฉัน (หรือคนอื่น) ต้องทำอีกครั้งในอนาคต มันเรียกว่าba64

เพียงแค่ใส่ก็จะใช้ URL ข้อมูลที่มีภาพที่เข้ารหัส Base64 และบันทึกภาพไปยังระบบไฟล์ของคุณ มันสามารถบันทึกพร้อมกันหรือไม่พร้อมกัน นอกจากนี้ยังมีฟังก์ชันตัวช่วยสองฟังก์ชันหนึ่งฟังก์ชันหนึ่งเพื่อรับนามสกุลไฟล์ของรูปภาพและอีกฟังก์ชันหนึ่งเพื่อแยกการเข้ารหัส Base64 ออกจากdata:คำนำหน้าโครงการ

นี่คือตัวอย่าง:

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

ติดตั้ง: npm i ba64 -S. Repo อยู่บน GitHub: https://github.com/HarryStevens/ba64

PS มันเกิดขึ้นกับฉันในภายหลังว่า ba64 อาจเป็นชื่อที่ไม่ดีสำหรับโมดูลเนื่องจากผู้คนอาจคิดว่ามันเป็นการเข้ารหัสและถอดรหัส Base64 ซึ่งไม่ได้เป็นเช่นนั้น โอ้ดี


2

สิ่งนี้ทำให้ฉันได้อย่างง่ายดายและสมบูรณ์แบบ

คำอธิบายที่ยอดเยี่ยมโดย Scott Robinson

จากภาพเป็นสตริงเบส 64

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

จากสตริง base64 เป็นรูปภาพ

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

1

วิธีง่ายๆในการแปลงภาพbase64เป็นไฟล์และบันทึกเป็น ID หรือชื่อแบบสุ่ม

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

การแปลงไฟล์จากสตริง base64 เป็นอิมเมจ png

4 ตัวแปรที่ใช้งานได้

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

ฟังก์ชั่นด้านล่างเพื่อบันทึกไฟล์เพียงแค่ส่งไฟล์ base64 ของคุณแล้วส่งคืนชื่อไฟล์บันทึกใน 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;
}

1
ทำงานให้ฉัน และสามารถใช้สำหรับการแปลง base64 ใด ๆ มันปฏิบัติต่อทุกไฟล์โดยทั่วไป ขอบคุณ!
Guilherme Sampaio

1

คุณสามารถใช้ห้องสมุดบุคคลที่สามเช่นbase64-imgหรือbase64 ต่อภาพ

  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 ต่อภาพ
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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.