NodeJS: Lưu hình ảnh được mã hóa base64 vào đĩa


162

Ứng dụng My Express đang nhận được một PNG được mã hóa base64 từ trình duyệt (được tạo từ canvas với toDataURL ()) và ghi nó vào một tệp. Nhưng tệp không phải là tệp hình ảnh hợp lệ và tiện ích "tệp" chỉ đơn giản xác định đó là "dữ liệu".

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
Tôi đã cập nhật câu trả lời mà tôi nghĩ là những gì bạn cần ở nơi đầu tiên;)
Alfred

Rõ ràng đây không phải là những gì bạn yêu cầu, nhưng (trong trường hợp của tôi) tôi nhận ra rằng cách tiếp cận tốt nhất chỉ là lưu trữ toàn bộ chuỗi được mã hóa vào cơ sở dữ liệu của tôi (bạn luôn có thể tải nó bằng cách sử dụng <img src="data:image/png;base64,..." />). Chỉ là một tùy chọn để xem xét cho những người khác sử dụng chủ đề này như một tài liệu tham khảo.
JSideris

Câu trả lời:


324

Tôi nghĩ rằng bạn đang chuyển đổi dữ liệu nhiều hơn một chút so với bạn cần. Khi bạn tạo bộ đệm với mã hóa phù hợp, bạn chỉ cần ghi bộ đệm vào tệp.

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

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

Bộ đệm mới (..., 'base64') sẽ chuyển đổi chuỗi đầu vào thành Bộ đệm, đây chỉ là một mảng byte, bằng cách hiểu đầu vào là một chuỗi được mã hóa base64. Sau đó, bạn có thể chỉ cần viết mảng byte đó vào tệp.

Cập nhật

Như đã đề cập trong các ý kiến, req.rawBodykhông còn là một điều. Nếu bạn đang sử dụng express/ connectthì bạn nên sử dụng bodyParser()phần mềm trung gian và sử dụng req.body, và nếu bạn đang làm điều này bằng cách sử dụng Node tiêu chuẩn thì bạn cần tổng hợp các đối tượng datasự kiện đến Buffervà thực hiện phân tích dữ liệu hình ảnh này trong cuộc endgọi lại.


2
Ngoài ra, có một lỗi đánh máy nhỏ trong đối số writeFile trong ví dụ của bạn: "bufferData" -> "dataBuffer".
mahemoff

@RJ. req.rawBodychứa dữ liệu yêu cầu được mã hóa dưới dạng URL dữ liệu: developer.mozilla.org/en-US/docs/data_URIs . Vì vậy, bạn phải loại bỏ phần đầu để lấy dữ liệu base64 để lưu.
loganfsmyth

2
Đây là công cụ tuyệt vời, cảm ơn! Đối với những người tìm thấy điều này trong tương lai, rawBody không còn là tài sản của req. Bạn phải sử dụng phần mềm trung gian phân tích cú pháp cơ thể nhanh để lấy dữ liệu.
DigitalDesignDj

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

@notgiorgi Tốt nhất nên hỏi một câu hỏi mới với đủ chi tiết để tái tạo vấn đề của bạn và liên kết với câu hỏi này nói rằng bạn không thể làm cho nó hoạt động được.
loganfsmyth

22

đây là giải pháp đầy đủ của tôi, nó sẽ đọc bất kỳ định dạng hình ảnh cơ sở 64 và lưu nó ở định dạng thích hợp trong cơ sở dữ liệu:

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

có ai ở đây để trả lời tôi không ?? về vấn đề này??
iam

tôi vừa sửa đổi mã của bạn fs.writeFile ("test.jpg", imageBuffer.data, function (err) {json_response ['thành công'] = true; res.json (json_response);}); hình ảnh được tải lên nhưng kết quả không giống với tôi .. lỗi: 502 Cổng xấu thực sự có vấn đề trong res.json, tại sao điều này không được in ...
iam

18

CẬP NHẬT

Tôi tìm thấy liên kết thú vị này làm thế nào để giải quyết vấn đề của bạn trong PHP . Tôi nghĩ rằng bạn quên thay thế spacebởi +như thể hiện trong liên kết.

Tôi lấy vòng tròn này từ http://images-mediawiki-sites.thefullwiki.org/04/1/7/5/6204600836255205.png làm mẫu giống như:

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

Tiếp theo, tôi đặt nó thông qua http://www.greywyvern.com/code/php/binary2base64 đã trả lại cho tôi:

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

đã lưu chuỗi này base64mà tôi đọc từ mã của mình.

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

Tôi nhận được một vòng tròn trở lại, nhưng điều buồn cười là kích thước tệp đã thay đổi :) ...

KẾT THÚC

Khi bạn đọc lại hình ảnh tôi nghĩ bạn cần thiết lập các tiêu đề

Lấy ví dụ imagepng từ trang PHP:

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

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

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

Tôi nghĩ rằng dòng thứ hai header('Content-Type: image/png');, quan trọng là hình ảnh của bạn sẽ không được hiển thị trong trình duyệt, nhưng chỉ một loạt dữ liệu nhị phân được hiển thị cho trình duyệt.

Trong Express bạn chỉ cần sử dụng một cái gì đó như dưới đây. Tôi sẽ hiển thị gravatar của bạn được đặt tại http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG và là tệp jpeg khi bạn curl --head http://www.gravatar.com/avatar/cabf735ce7b8b4471ef46ea54f71832d?s=32&d=identicon&r=PG. Tôi chỉ yêu cầu các tiêu đề vì các curl khác sẽ hiển thị một loạt các nội dung nhị phân (Google Chrome sẽ ngay lập tức tải xuống) cho bảng điều khiển:

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

Cảm ơn Alfred, nhưng trong trường hợp thử nghiệm tối thiểu này, tôi sẽ không gửi lại bất cứ thứ gì từ máy chủ. Tôi chỉ đơn giản là ghi tệp vào đĩa trên máy chủ và có vẻ như chính tệp đó không phải là hình ảnh hợp lệ. Tôi khá chắc chắn rằng Base64 là đúng, nhưng dường như có một vấn đề khi viết nó ra dưới dạng nhị phân.
mahemoff

1
Xin lỗi tôi hiểu nhầm câu hỏi: $. Tôi sẽ thử lại.
Alfred

1
Cảm ơn đã cập nhật, nhưng việc thay thế không gian không phù hợp với tôi và thực sự không cần thiết khi tôi áp dụng giải pháp của Logan. Để tham khảo, canvas rất đơn giản trong trường hợp thử nghiệm của tôi: var bối cảnh = canvas.getContext ('2d'); bối cảnh.fillStyle = "# f89"; bối cảnh.fillRect (50,50,100,100);
mahemoff

Được rồi vì tôi đã lấy lại được hình ảnh khi tôi làm điều này, nhưng ít nhất vấn đề của bạn đã được giải quyết: P
Alfred

Thật thú vị, không chắc tại sao toString ("nhị phân") không làm hỏng nó trong trường hợp của bạn. Trong mọi trường hợp, không gian tự nhiên không nên xuất hiện trong cơ sở64, vì vậy, thay thế sẽ là tranh luận. Đó là với ví dụ tôi đã cung cấp. (Tôi đã làm thử một biến thể với dòng mới bằng tay chèn, sau khi đọc spec MIME đòi hỏi dòng không lớn hơn 72 ký tự, chủ yếu ra khỏi hoang tưởng ... nó quay ra để làm việc với hoặc nếu không có sự newlines, miễn là toString ( "nhị phân" ) bị bỏ.)
mahemoff

6

Tôi cũng đã phải lưu hình ảnh được mã hóa Base64 là một phần của URL dữ liệu, vì vậy cuối cùng tôi đã tạo ra một mô-đun npm nhỏ để làm điều đó trong trường hợp tôi (hoặc người khác) cần làm lại trong tương lai. Nó được gọi là ba64 .

Nói một cách đơn giản, nó lấy một URL dữ liệu với hình ảnh được mã hóa Base64 và lưu hình ảnh vào hệ thống tệp của bạn. Nó có thể lưu đồng bộ hoặc không đồng bộ. Nó cũng có hai hàm trợ giúp, một để lấy phần mở rộng tệp của ảnh và cái còn lại để tách mã hóa Base64 khỏi data:tiền tố lược đồ.

Đây là một ví dụ:

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

Cài đặt nó : npm i ba64 -S. Repo có trên GitHub: https://github.com/HarryStevens/ba64 .

PS Sau đó, tôi nhận ra rằng ba64 có thể là một tên xấu cho mô-đun vì mọi người có thể cho rằng nó thực hiện mã hóa và giải mã Base64, điều này không có (có rất nhiều mô-đun đã làm điều đó). Ồ tốt


2

Điều này đã làm điều đó cho tôi đơn giản và hoàn hảo.

Scott Robinson giải thích tuyệt vời

Từ hình ảnh đến chuỗi base64

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

Từ chuỗi base64 đến hình ảnh

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

1

Cách dễ dàng để chuyển đổi hình ảnh base64 thành tập tin và lưu dưới dạng một số id hoặc tên ngẫu nhiên.

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

Chuyển đổi từ tập tin với chuỗi base64 sang hình ảnh png.

4 biến thể hoạt động.

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

Bên dưới chức năng lưu tệp, chỉ cần truyền tệp base64 của bạn, nó trả lại tên tệp lưu trong 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
Đã làm cho tôi. Và nó có thể được sử dụng cho bất kỳ chuyển đổi cơ sở64. Nó xử lý mọi tập tin một cách khái quát. Cảm ơn bạn!
Guilherme Sampaio

1

Bạn có thể sử dụng thư viện của bên thứ ba như base64-img hoặc base64-to-image .

  1. cơ sở64-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. hình ảnh cơ bản 64
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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.