701 Stimmen

Erstellen eines BLOB aus einem Base64-String in JavaScript

Ich habe Base64-codierte Binärdaten in einem String:

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

Ich möchte eine blob: URL erstellen, die diese Daten enthält, und sie dem Benutzer anzeigen:

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

window.location = blobUrl;

Ich konnte bisher nicht herausfinden, wie ich den BLOB erstellen kann.

In einigen Fällen kann ich dies vermeiden, indem ich stattdessen eine data: URL verwende:

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

window.location = dataUrl;

In den meisten Fällen sind die data: URLs jedoch unpraktisch groß.


Wie kann ich einen Base64-String in ein BLOB-Objekt in JavaScript dekodieren?

1108voto

Jeremy Punkte 1

Die atob Funktion entschlüsselt einen Base64-codierten String in einen neuen String mit einem Zeichen für jedes Byte der Binärdaten.

const byteCharacters = atob(b64Data);

Der Codepunkt (charCode) jedes Zeichens entspricht dem Wert des Bytes. Wir können ein Array von Byte-Werten erstellen, indem wir dies für jedes Zeichen im String mit der Methode .charCodeAt anwenden.

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

Dieses Array von Byte-Werten kann in ein echtes Byten-Array umgewandelt werden, indem es dem Konstruktor Uint8Array übergeben wird.

const byteArray = new Uint8Array(byteNumbers);

Dies wiederum kann in ein BLOB umgewandelt werden, indem es in ein Array eingewickelt und dem Konstruktor Blob übergeben wird.

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

Der obige Code funktioniert. Die Leistung kann jedoch verbessert werden, indem die byteCharacters in kleineren Slices verarbeitet werden, anstatt alles auf einmal zu verarbeiten. In meinen groben Tests scheinen 512 Bytes eine gute Slicegröße zu sein. Dies gibt uns die folgende Funktion.

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;

Vollständiges Beispiel:

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 contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';

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

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

582voto

Endless Punkte 28715

Hier ist eine minimalistischere Methode ohne Abhängigkeiten oder Bibliotheken.
Es erfordert die neue fetch API. (Kann ich es verwenden?)

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

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

Mit dieser Methode können Sie auch einfach ein ReadableStream, ArrayBuffer, Text und JSON erhalten.
(fyi das funktioniert auch mit node-fetch in Node)

Als Funktion:

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

Aber ich würde Sie ermutigen, base64 überhaupt nicht zu verwenden. Es gibt bessere Möglichkeiten, binäre Daten zu senden und zu empfangen. JSON ist nicht immer die beste Option. Es verbraucht mehr Bandbreite und verbraucht mehr Rechenzeit für das (De-)Codieren von Sachen. Verwenden Sie z.B. canvas.toBlob anstelle von canvas.toDataURL und verwenden Sie FormData zum Senden binärer Dateien. Sie können auch ein mehrteiliges Payload zurücksenden und es mit await response.formData() decodieren, das von einer Serverantwort kommt. FormData funktioniert in beide Richtungen.


Ich habe einen einfachen Leistungstest gegen Jeremy's ES6-Synchronversion gemacht.
Die synchrone Version blockiert die UI eine Weile. Das Offenhalten des DevTools kann die Leistung des fetch verlangsamen

document.body.innerHTML += ''
// bekomme ein Dummy-Verlaufsbild
var img=function(){var a=document.createElement("canvas"),b=a.getContext("2d"),c=b.createLinearGradient(0,0,1500,1500);a.width=a.height=3000;c.addColorStop(0,"red");c.addColorStop(1,"blue");b.fillStyle=c;b.fillRect(0,0,a.width,a.height);return a.toDataURL()}();

async function perf() {

  const blob = await fetch(img).then(res => res.blob())
  // mache daraus eine dataURI
  const url = img
  const b64Data = url.split(',')[1]

  // Jeremys Lösung
  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;
  }

  // Blockierende Methode messen
  let i = 500
  console.time('blocking b64')
  while (i--) {
    await b64toBlob(b64Data)
  }
  console.timeEnd('blocking b64')

  // Nicht blockierende Methode messen
  i = 500

  // Damit die Funktion nicht jedes Mal neu erstellt wird
  const toBlob = res => res.blob()
  console.time('fetch')
  while (i--) {
    await fetch(url).then(toBlob)
  }
  console.timeEnd('fetch')
  console.log('fertig')
}

perf()

105voto

Bacher Punkte 1586

Optimierte (aber weniger lesbare) Implementierung:

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

36voto

KAUSHIK PARMAR Punkte 515

Folgender ist mein TypeScript-Code, der leicht in JavaScript umgewandelt werden kann und den du verwenden kannst

/**
 * Konvertieren von BASE64 zu BLOB
 * @param base64Image Geben Sie Base64-Bilddaten ein, um sie in BLOB umzuwandeln
 */
private convertBase64ToBlob(base64Image: string) {
  // In zwei Teile aufteilen
  const parts = base64Image.split(';base64,');

  // Den Inhaltstyp halten
  const imageType = parts[0].split(':')[1];

  // Base64-String decodieren
  const decodedData = window.atob(parts[1]);

  // Erstellen eines UNIT8ARRAYs der Größe wie die der Zeilendaten
  const uInt8Array = new Uint8Array(decodedData.length);

  // Alle Zeichenkodierung in uInt8Array einfügen
  for (let i = 0; i < decodedData.length; ++i) {
    uInt8Array[i] = decodedData.charCodeAt(i);
  }

  // BLOB-Bild nach der Konvertierung zurückgeben
  return new Blob([uInt8Array], { type: imageType });
}

23voto

Eyup Yusein Punkte 351

Für alle Copy-Paste-Liebhaber da draußen wie mich, hier ist eine fertige Download-Funktion, die in Chrome, Firefox und Edge funktioniert:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X