526

Tôi có dữ liệu nhị phân được mã hóa Base64 trong một chuỗi:

const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';

Tôi muốn tạo một blob:URL chứa dữ liệu này và hiển thị nó cho người dùng:

const blob = new Blob(????, {type: contentType});
const blobUrl = URL.createObjectURL(blob);

window.location = blobUrl;

Tôi chưa thể tìm ra cách tạo BLOB.

Trong một số trường hợp, tôi có thể tránh điều này bằng cách sử dụng data:URL thay thế:

const dataUrl = `data:${contentType};base64,${b64Data}`;

window.location = dataUrl;

Tuy nhiên, trong hầu hết các trường hợp, các data:URL quá lớn.


Làm cách nào để giải mã chuỗi Base64 thành đối tượng BLOB trong JavaScript?

|
898

Các atobchức năng sẽ giải mã một chuỗi Base64 mã hóa thành một chuỗi mới với một ký tự cho mỗi byte của dữ liệu nhị phân.

const byteCharacters = atob(b64Data);

Điểm mã của mỗi ký tự (charCode) sẽ là giá trị của byte. Chúng ta có thể tạo một mảng các giá trị byte bằng cách áp dụng điều này bằng cách sử dụng .charCodeAtphương thức cho mỗi ký tự trong chuỗi.

const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
}

Bạn có thể chuyển đổi mảng giá trị byte này thành một mảng byte được đánh kiểu thực bằng cách chuyển nó cho hàm Uint8Arraytạo.

const byteArray = new Uint8Array(byteNumbers);

Đến lượt nó, nó có thể được chuyển đổi thành BLOB bằng cách gói nó trong một mảng và chuyển nó cho hàm Blobtạo.

const blob = new Blob([byteArray], {type: contentType});

Đoạn mã trên hoạt động. Tuy nhiên, hiệu suất có thể được cải thiện một chút bằng cách xử lý các byteCharacterslát nhỏ hơn, thay vì tất cả cùng một lúc. Trong thử nghiệm thô của tôi, 512 byte có vẻ là một kích thước lát tốt. Điều này cung cấp cho chúng tôi chức năng sau đây.

const b64toBlob = (b64Data, contentType='', sliceSize=512) => {
  const byteCharacters = atob(b64Data);
  const byteArrays = [];

  for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
    const slice = byteCharacters.slice(offset, offset + sliceSize);

    const byteNumbers = new Array(slice.length);
    for (let i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }

    const byteArray = new Uint8Array(byteNumbers);
    byteArrays.push(byteArray);
  }

  const blob = new Blob(byteArrays, {type: contentType});
  return blob;
}
const blob = b64toBlob(b64Data, contentType);
const blobUrl = URL.createObjectURL(blob);

window.location = blobUrl;

Ví dụ đầy đủ:

|
  • 1
    Chào Jeremy. Chúng tôi đã có mã này trong ứng dụng web của mình và nó không gây ra bất kỳ sự cố nào cho đến khi các tệp được tải xuống có kích thước lớn hơn. Vì vậy, nó gây ra treo và treo máy chủ sản xuất, khi người dùng đang sử dụng Chrome hoặc IE để tải xuống các tệp lớn hơn 100mb. Chúng tôi nhận thấy rằng dòng sau trong IE đang tăng ngoại lệ bộ nhớ "var byteNumbers = new Array (slice.length)". Tuy nhiên trong chrome, đó là vòng lặp for gây ra vấn đề tương tự. Chúng tôi không thể tìm thấy giải pháp thích hợp cho vấn đề này, sau đó chúng tôi chuyển sang tải trực tiếp tệp xuống bằng window.open. Bạn có thể cung cấp một số trợ giúp ở đây? –  10:02:13 29/03/2018
  • 1
    này không làm việc cho tôi đối với một số đốm màu trên Chrome và Firefox nhưng làm việc trên mép: / –  08:57:07 25/06/2019
  • 1
    Mỗi khi tôi tìm thấy điều gì đó để làm trong javascript, tôi lại thấy nó kinh khủng hơn, kinh khủng và kinh khủng hơn. Thật là một bộ sưu tập tê liệt của hacks jurily gian lận togheter –  10:47:37 02/10/2019
  • 1
    câu trả lời chính xác. Thanks a lot –  12:37:49 31/08/2020
  • 1
    Cứu cuộc đời tôi. Tôi đã phát điên khi cố gắng thực hiện tải xuống tệp excel. –  06:26:32 08/09/2020
372

Đây là một phương pháp tối giản hơn mà không có bất kỳ phụ thuộc hoặc thư viện nào.
Nó yêu cầu API tìm nạp mới. ( Tôi có thể sử dụng nó không? )

var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="

fetch(url)
.then(res => res.blob())
.then(console.log)

Với phương pháp này, bạn cũng có thể dễ dàng nhận được ReadableStream, ArrayBuffer, text và JSON.
(fyi điều này cũng hoạt động với tìm nạp nút trong Node)

Như một chức năng:

const b64toBlob = (base64, type = 'application/octet-stream') => 
  fetch(`data:${type};base64,${base64}`).then(res => res.blob())

Tôi đã thực hiện một bài kiểm tra hiệu suất đơn giản đối với phiên bản đồng bộ hóa ES6 của Jeremy.
Phiên bản đồng bộ hóa sẽ chặn giao diện người dùng trong một thời gian. giữ devtool mở có thể làm chậm hiệu suất tìm nạp

|
  • 1
    Điều này sẽ vẫn hoạt động nếu kích thước của chuỗi mã hóa base64 lớn, giả sử lớn hơn 665536 ký tự, đó là giới hạn cho kích thước URI trong Opera? –  17:54:21 24/10/2017
  • 1
    Tôi không biết, tôi biết nó có thể là một giới hạn đối với thanh địa chỉ nhưng làm mọi thứ với AJAX có thể là một ngoại lệ vì nó không phải được hiển thị. Bạn phải kiểm tra nó. Nếu nó ở nơi tôi, tôi sẽ không bao giờ nhận được chuỗi base64 ngay từ đầu. Nghĩ rằng đó là một thực hành xấu, chiếm nhiều bộ nhớ và thời gian hơn để giải mã và mã hóa. createObjectURLthay vì readAsDataURLlà tốt hơn nhiều chẳng hạn. Và nếu bạn tải lên các file sử dụng ajax, chọn FormDatathay vì JSON, hoặc sử dụng canvas.toBlobthay vìtoDataURL –  20:19:53 24/10/2017
  • 1
    Thậm chí tốt hơn như inline:await (await fetch(imageDataURL)).blob() –  09:10:53 10/09/2018
  • 1
    chắc chắn, nếu bạn nhắm mục tiêu trình duyệt mới nhất. Nhưng điều đó đòi hỏi hàm cũng phải ở bên trong một hàm không đồng bộ. Nói về ... await fetch(url).then(r=>r.blob())được sorter –  20:38:28 10/09/2018
  • 1
    Giải pháp rất gọn gàng, nhưng theo hiểu biết của tôi sẽ không hoạt động với IE (với polyfill ofc) do Access is denied.lỗi. Tôi đoán fetchđể lộ blob dưới url blob - theo cách tương tự URL.createObjectUrl- sẽ không hoạt động trên ie11. tài liệu tham khảo . Có thể có một số cách giải quyết để sử dụng tìm nạp với IE11? Có vẻ tốt hơn nhiều so với các giải pháp đồng bộ khác :) –  08:10:29 04/01/2019
86

Triển khai được tối ưu hóa (nhưng ít đọc hơn):

function base64toBlob(base64Data, contentType) {
    contentType = contentType || '';
    var sliceSize = 1024;
    var byteCharacters = atob(base64Data);
    var bytesLength = byteCharacters.length;
    var slicesCount = Math.ceil(bytesLength / sliceSize);
    var byteArrays = new Array(slicesCount);

    for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
        var begin = sliceIndex * sliceSize;
        var end = Math.min(begin + sliceSize, bytesLength);

        var bytes = new Array(end - begin);
        for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
            bytes[i] = byteCharacters[offset].charCodeAt(0);
        }
        byteArrays[sliceIndex] = new Uint8Array(bytes);
    }
    return new Blob(byteArrays, { type: contentType });
}
|
  • 1
    Có lý do gì để cắt các byte thành các đốm màu không? Nếu tôi không sử dụng, có bất lợi hoặc rủi ro nào không? –  03:21:26 24/09/2015
  • 1
    Hoạt động tốt trên Android với Ionic 1 / Angular 1. Yêu cầu phải có Slice, nếu không, tôi gặp phải tình trạng OOM (Android 6.0.1). –  13:16:06 03/03/2017
  • 1
    Chỉ có ví dụ ngoài kia mà tôi có thể làm việc liên tục với bất kỳ loại tài liệu nào trong môi trường doanh nghiệp ở cả IE 11 và Chrome. –  15:27:40 22/02/2018
  • 1
    Cái này thật tuyệt. Cảm ơn bạn! –  20:09:44 05/09/2019
  • 1
    Một lời giải thích sẽ được theo thứ tự. Ví dụ: tại sao nó có hiệu suất cao hơn? –  14:02:57 13/04/2020
20

Đối với tất cả các hỗ trợ của trình duyệt, đặc biệt là trên Android, có lẽ bạn có thể thêm điều này:

try{
    blob = new Blob(byteArrays, {type : contentType});
}
catch(e){
    // TypeError old Google Chrome and Firefox
    window.BlobBuilder = window.BlobBuilder ||
                         window.WebKitBlobBuilder ||
                         window.MozBlobBuilder ||
                         window.MSBlobBuilder;
    if(e.name == 'TypeError' && window.BlobBuilder){
        var bb = new BlobBuilder();
        bb.append(byteArrays);
        blob = bb.getBlob(contentType);
    }
    else if(e.name == "InvalidStateError"){
        // InvalidStateError (tested on FF13 WinXP)
        blob = new Blob(byteArrays, {type : contentType});
    }
    else{
        // We're screwed, blob constructor unsupported entirely
    }
}
|
  • 1
    Cảm ơn bạn, nhưng có HAI vấn đề trên đoạn mã bạn đã viết ở trên nếu tôi đọc đúng: (1) Mã trong catch () trên else-if cuối cùng giống với mã gốc trong try (): "blob = new Blob (byteArrays, {type: contentType}) "... Tôi không hiểu tại sao bạn đề xuất lặp lại cùng một mã sau ngoại lệ ban đầu? ... (2) BlobBuilder.append () KHÔNG thể chấp nhận mảng byte trừ ArrayBuffer. Vì vậy, các byte-mảng đầu vào phải được chuyển đổi thêm thành ArrayBuffer của nó trước khi sử dụng API này. REF: developer.mozilla.org/en-US/docs/Web/API/BlobBuilder –  19:55:53 26/04/2020
18

Xem ví dụ này: https://jsfiddle.net/pqhdce2L/

function b64toBlob(b64Data, contentType, sliceSize) {
  contentType = contentType || '';
  sliceSize = sliceSize || 512;

  var byteCharacters = atob(b64Data);
  var byteArrays = [];

  for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
    var slice = byteCharacters.slice(offset, offset + sliceSize);

    var byteNumbers = new Array(slice.length);
    for (var i = 0; i < slice.length; i++) {
      byteNumbers[i] = slice.charCodeAt(i);
    }

    var byteArray = new Uint8Array(byteNumbers);

    byteArrays.push(byteArray);
  }
    
  var blob = new Blob(byteArrays, {type: contentType});
  return blob;
}


var contentType = 'image/png';
var b64Data = Your Base64 encode;

var blob = b64toBlob(b64Data, contentType);
var blobUrl = URL.createObjectURL(blob);

var img = document.createElement('img');
img.src = blobUrl;
document.body.appendChild(img);

|
15

Đối với dữ liệu hình ảnh, tôi thấy nó đơn giản hơn để sử dụng canvas.toBlob(không đồng bộ)

function b64toBlob(b64, onsuccess, onerror) {
    var img = new Image();

    img.onerror = onerror;

    img.onload = function onload() {
        var canvas = document.createElement('canvas');
        canvas.width = img.width;
        canvas.height = img.height;

        var ctx = canvas.getContext('2d');
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

        canvas.toBlob(onsuccess);
    };

    img.src = b64;
}

var base64Data = 'data:image/jpg;base64,/9j/4AAQSkZJRgABAQA...';
b64toBlob(base64Data,
    function(blob) {
        var url = window.URL.createObjectURL(blob);
        // do something with url
    }, function(error) {
        // handle error
    });
|
  • 1
    Tôi đoán bạn mất một số thông tin với điều đó ... như các thông tin meta của nó như chuyển đổi bất kỳ hình ảnh để png, vì vậy nó không phải là kết quả tương tự, cũng có thể đây chỉ hoạt động cho hình ảnh –  16:07:46 13/09/2014
  • 1
    Tôi đoán bạn có thể cải thiện nó bằng cách trích xuất loại hình ảnh image/jpgtừ chuỗi base64 và sau đó chuyển nó dưới dạng tham số thứ hai vào toBlobhàm để kết quả là cùng loại. Ngoài ra, tôi nghĩ điều này là hoàn hảo - nó tiết kiệm 30% lưu lượng truy cập và không gian đĩa của bạn trên máy chủ (so với base64) và nó hoạt động tốt ngay cả với PNG trong suốt. –  06:30:54 20/06/2018
  • 1
    Chức năng treo với những hình ảnh lớn hơn 2MB ... trong Android tôi nhận được ngoại lệ: android.os.TransactionTooLarge –  22:09:21 29/01/2019
14

Sau đây là mã TypeScript của tôi có thể được chuyển đổi dễ dàng thành JavaScript và bạn có thể sử dụng

/**
 * Convert BASE64 to BLOB
 * @param base64Image Pass Base64 image data to convert into the BLOB
 */
private convertBase64ToBlob(base64Image: string) {
  // Split into two parts
  const parts = base64Image.split(';base64,');

  // Hold the content type
  const imageType = parts[0].split(':')[1];

  // Decode Base64 string
  const decodedData = window.atob(parts[1]);

  // Create UNIT8ARRAY of size same as row data length
  const uInt8Array = new Uint8Array(decodedData.length);

  // Insert all character code into uInt8Array
  for (let i = 0; i < decodedData.length; ++i) {
    uInt8Array[i] = decodedData.charCodeAt(i);
  }

  // Return BLOB image after conversion
  return new Blob([uInt8Array], { type: imageType });
}
|
  • 1
    Mặc dù đoạn mã này có thể là giải pháp, nhưng bao gồm phần giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho người đọc trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn. –  09:38:14 20/03/2019
  • 1
    Ngoài ra, tại sao bạn lại VÀNG khi nhận xét? –  06:04:54 17/12/2019
  • 1
    Typescript codeMã của bạn chỉ có một loại DUY NHẤT và loại đó là như vậy any. Như tại sao thậm chí còn bận tâm ?? –  14:02:22 12/02/2020
14

Đối với tất cả những người yêu thích sao chép-dán ngoài kia như tôi, đây là một chức năng tải xuống nấu chín hoạt động trên Chrome, Firefox và Edge:

window.saveFile = function (bytesBase64, mimeType, fileName) {
var fileUrl = "data:" + mimeType + ";base64," + bytesBase64;
fetch(fileUrl)
    .then(response => response.blob())
    .then(blob => {
        var link = window.document.createElement("a");
        link.href = window.URL.createObjectURL(blob, { type: mimeType });
        link.download = fileName;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    });
}
|
12

Tôi đang đăng một cách khai báo hơn về chuyển đổi Base64 đồng bộ hóa. Mặc dù async fetch().blob()rất gọn gàng và tôi rất thích giải pháp này, nhưng nó không hoạt động trên Internet Explorer 11 (và có thể là cả Edge - tôi chưa thử nghiệm cái này), ngay cả với polyfill - hãy xem nhận xét của tôi về Endless ' đăng để biết thêm chi tiết.

const blobPdfFromBase64String = base64String => {
   const byteArray = Uint8Array.from(
     atob(base64String)
       .split('')
       .map(char => char.charCodeAt(0))
   );
  return new Blob([byteArray], { type: 'application/pdf' });
};

Tặng kem

Nếu bạn muốn in nó, bạn có thể làm như sau:

const isIE11 = !!(window.navigator && window.navigator.msSaveOrOpenBlob); // Or however you want to check it
const printPDF = blob => {
   try {
     isIE11
       ? window.navigator.msSaveOrOpenBlob(blob, 'documents.pdf')
       : printJS(URL.createObjectURL(blob)); // http://printjs.crabbly.com/
   } catch (e) {
     throw PDFError;
   }
};

Bonus x 2 - Mở tệp BLOB trong tab mới cho Internet Explorer 11

Nếu bạn có thể thực hiện một số xử lý trước chuỗi Base64 trên máy chủ, bạn có thể hiển thị nó dưới một số URL và sử dụng liên kết trong printJS:)

|
  • 1
    có thể thông báo cho bạn rằng công trình Edge, Edge được tại Chromium dựa vì thế nên hỗ trợ hầu hết mọi thứ chrome có thể làm –  19:38:37 27/09/2020
10

Tôi nhận thấy rằng Internet Explorer 11 cực kỳ chậm khi cắt dữ liệu như Jeremy đề xuất. Điều này đúng với Chrome, nhưng Internet Explorer dường như gặp sự cố khi chuyển dữ liệu đã cắt đến Blob-Constructor. Trên máy tính của tôi, việc truyền 5 MB dữ liệu khiến Internet Explorer gặp sự cố và mức tiêu thụ bộ nhớ đang tăng dần. Chrome tạo ra đốm màu nhanh chóng.

Chạy mã này để so sánh:

var byteArrays = [],
    megaBytes = 2,
    byteArray = new Uint8Array(megaBytes*1024*1024),
    block,
    blobSlowOnIE, blobFastOnIE,
    i;

for (i = 0; i < (megaBytes*1024); i++) {
    block = new Uint8Array(1024);
    byteArrays.push(block);
}

//debugger;

console.profile("No Slices");
blobSlowOnIE = new Blob(byteArrays, { type: 'text/plain'});
console.profileEnd();

console.profile("Slices");
blobFastOnIE = new Blob([byteArray], { type: 'text/plain'});
console.profileEnd();

Vì vậy, tôi quyết định bao gồm cả hai phương pháp được mô tả bởi Jeremy trong một hàm. Tín dụng thuộc về anh ta cho điều này.

function base64toBlob(base64Data, contentType, sliceSize) {

    var byteCharacters,
        byteArray,
        byteNumbers,
        blobData,
        blob;

    contentType = contentType || '';

    byteCharacters = atob(base64Data);

    // Get BLOB data sliced or not
    blobData = sliceSize ? getBlobDataSliced() : getBlobDataAtOnce();

    blob = new Blob(blobData, { type: contentType });

    return blob;


    /*
     * Get BLOB data in one slice.
     * => Fast in Internet Explorer on new Blob(...)
     */
    function getBlobDataAtOnce() {
        byteNumbers = new Array(byteCharacters.length);

        for (var i = 0; i < byteCharacters.length; i++) {
            byteNumbers[i] = byteCharacters.charCodeAt(i);
        }

        byteArray = new Uint8Array(byteNumbers);

        return [byteArray];
    }

    /*
     * Get BLOB data in multiple slices.
     * => Slow in Internet Explorer on new Blob(...)
     */
    function getBlobDataSliced() {

        var slice,
            byteArrays = [];

        for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
            slice = byteCharacters.slice(offset, offset + sliceSize);

            byteNumbers = new Array(slice.length);

            for (var i = 0; i < slice.length; i++) {
                byteNumbers[i] = slice.charCodeAt(i);
            }

            byteArray = new Uint8Array(byteNumbers);

            // Add slice
            byteArrays.push(byteArray);
        }

        return byteArrays;
    }
}
|
  • 1
    Cảm ơn bạn đã bao gồm điều này. Với bản cập nhật gần đây cho IE11 (từ 5/2016 đến 8/2016), việc tạo các đốm màu từ các mảng bắt đầu chiếm dung lượng ram lớn hơn rất nhiều. Bằng cách gửi một Uint8Array duy nhất vào trình tạo blog, nó hầu như không sử dụng ram và thực sự đã hoàn thành quá trình. –  18:05:29 11/08/2016
  • 1
    Việc tăng kích thước lát cắt trong mẫu thử nghiệm từ 1K lên 8..16K làm giảm đáng kể thời gian trong IE. Trên máy tính của tôi mã ban đầu mất 5-8 giây, mã với 8K khối chỉ mất 356ms và 225ms cho 16K khối –  10:33:49 14/04/2017
8

Nếu bạn có thể thêm một phần phụ thuộc vào dự án của mình, thì có blob-utilgói npm tuyệt vời cung cấp một base64StringToBlobchức năng tiện dụng . Sau khi được thêm vào của package.jsonbạn, bạn có thể sử dụng nó như thế này:

import { base64StringToBlob } from 'blob-util';

const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';

const blob = base64StringToBlob(b64Data, contentType);

// Do whatever you need with your blob...
|
4

Phương thức có tìm nạp là giải pháp tốt nhất, nhưng nếu ai cần sử dụng phương thức không tìm nạp thì đây là nó, vì những phương pháp đã đề cập trước đây không hoạt động với tôi:

function makeblob(dataURL) {
    const BASE64_MARKER = ';base64,';
    const parts = dataURL.split(BASE64_MARKER);
    const contentType = parts[0].split(':')[1];
    const raw = window.atob(parts[1]);
    const rawLength = raw.length;
    const uInt8Array = new Uint8Array(rawLength);

    for (let i = 0; i < rawLength; ++i) {
        uInt8Array[i] = raw.charCodeAt(i);
    }

    return new Blob([uInt8Array], { type: contentType });
}
|

Câu trả lời của bạn (> 20 ký tự)

Bằng cách click "Đăng trả lời", bạn đồng ý với Điều khoản dịch vụ, Chính sách bảo mật and Chính sách cookie của chúng tôi.

Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ hoặc hỏi câu hỏi của bạn.