文字列にbase64でエンコードされたバイナリデータがあります。
const contentType = 'image/png';
const b64Data = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
このデータを含むblob:
URLを作成してユーザーに表示します。
const blob = new Blob(????, {type: contentType});
const blobUrl = URL.createObjectURL(blob);
window.location = blobUrl;
私はBlob
を作成する方法を理解することができませんでした。
場合によっては、代わりにdata:
URLを使用することでこれを回避できます。
const dataUrl = `data:${contentType};base64,${b64Data}`;
window.location = dataUrl;
しかしほとんどの場合、data:
のURLは極端に大きいです。
JavaScriptでbase64文字列をBlob
オブジェクトにデコードする方法を教えてください。
atob
関数は、base64でエンコードされた文字列をバイナリデータの各バイトの文字を持つ新しい文字列にデコードします。
const byteCharacters = atob(b64Data);
各文字のコードポイント(charCode)はバイトの値になります。文字列の各文字に対して.charCodeAt
メソッドを使用してこれを適用することで、バイト値の配列を作成できます。
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
このバイト値の配列をUint8Array
コンストラクタに渡すことで、実際の型付きバイト配列に変換できます。
const byteArray = new Uint8Array(byteNumbers);
これはそれを配列でラップしてBlob
コンストラクタに渡すことでBlob
に変換できます。
const blob = new Blob([byteArray], {type: contentType});
上記のコードは機能します。しかし、パフォーマンスは一度に全部ではなく、より小さなスライスでbyteCharacters
を処理することによって少し改善することができます。私の大まかなテストでは512バイトが良いスライスサイズのようです。これにより、次のような機能が得られます。
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;
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);
これは、依存関係やライブラリを使用しない、より最小限の方法です。
新しい取得APIが必要です。 ( 使えますか? )
var url = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
fetch(url)
.then(res => res.blob())
.then(blob => console.log(blob))
このメソッドを使えば、ArrayBuffer、text、そしてJSONも簡単に取得できます。
非同期機能として:
const b64toBlob = async (b64Data, contentType='application/octet-stream') => {
const url = `data:${contentType};base64,${b64Data}`;
const response = await fetch(url);
const blob = await response.blob();
return blob;
};
JeremyのES6同期バージョンに対して簡単なパフォーマンステストを行いました。
同期バージョンはしばらくUIをブロックします。開発ツールを開いたままにしておくと、フェッチパフォーマンスが低下する可能性があります。
document.body.innerHTML += '<input autofocus placeholder="try writing">'
// get some dummy gradient image
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())
// turn it to a dataURI
const url = img
const b64Data = url.split(',')[1]
// Jeremy Banks solution
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;
}
// bench blocking method
let i = 1000
console.time('b64')
while (i--) {
await b64toBlob(b64Data)
}
console.timeEnd('b64')
// bench non blocking
i = 1000
// so that the function is not reconstructed each time
const toBlob = res => res.blob()
console.time('fetch')
while (i--) {
await fetch(url).then(toBlob)
}
console.timeEnd('fetch')
console.log('done')
}
perf()
最適化された(しかし読みにくい)実装:
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 });
}
特にAndroid上のすべてのブラウザサポート用。おそらくあなたはこれを追加することができます
try{
blob = new Blob( byteArrays, {type : contentType});
}
catch(e){
// TypeError old chrome and FF
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
}
}
画像データについては、canvas.toBlob
を使うほうが簡単だと思います(非同期)。
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
});
この例を参照してください。 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);
私は、Internet Explorer 11がjeremyが提案したようにデータをスライスすると非常に遅くなることに気付きました。これはChromeにも当てはまりますが、スライスされたデータをBlob-Constructorに渡すときにIEに問題があるようです。私のマシンでは、5 MBのデータを渡すとIEがクラッシュし、メモリの消費が急増しています。 ChromeはすぐにBLOBを作成します。
比較のためにこのコードを実行してください。
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();
そのため、jeremyによって記述された両方の方法を1つの関数に含めることにしました。クレジットはこれのために彼に行きます。
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 IE 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 IE 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つの依存関係を追加するのに耐えることができれば、便利なblob-util
関数を提供するすばらしい base64StringToBlob
npmパッケージ があります。 package.json
に追加したら、次のように使用できます。
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...
私は同期base64変換の宣言的な方法を投稿しています。 async fetch().blob()
がとてもきちんとしていて、私がこの解決策をとても気に入っているとき、それはIE11ではうまくいかない(そしておそらくEdge - これをテストしていない) - 詳細についてはEndless postへの私のコメントを見てください。 。
const blobPdfFromBase64String = base64String => {
const byteArray = Uint8Array.from(
atob(base64String)
.split('')
.map(char => char.charCodeAt(0))
);
return new Blob([byteArray], { type: 'application/pdf' });
};
ボーナス
印刷したい場合は、次のようにします。
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;
}
};
BONUSx2 - IE11用の新しいタブでBLOBファイルを開く
サーバ上でbase64文字列の前処理を実行できる場合は、それを何らかのURLで公開してprintJS
:)のリンクを使用できます。
以下は私のTypeScriptコードで、簡単にJavaScriptに変換できます。
/**
* CONVERT BASE64 TO BLOB
* @param Base64Image Pass base64 image data to convert into the blob
*/
private convertBase64ToBlob(Base64Image: any) {
// 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 });
}
フェッチを伴うメソッドが最善の解決策ですが、フェッチを伴わずにメソッドを作成する必要がある場合は、上記の方法ではうまくいかなかったので、ここで解決します。
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 });
}