ArrayBufferからbase64でエンコードされた文字列


203

ArrayBufferマルチパートポストで使用する必要があるbase64文字列に変換する効率的な(ネイティブの読み取り)方法が必要です。

回答:


222
function _arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}

しかし、非ネイティブ実装はより高速です。例:https ://gist.github.com/958841 http://jsperf.com/encoding-xhr-image-data/6を 参照


10
リンクからネイティブでない実装を試したところ、1Mサイズのバッファーを変換するのに1分半かかりましたが、上記のループコードは1秒しかかかりませんでした。
cshu 2013年

1
このアプローチの単純さは気に入っていますが、そのすべての文字列の連結はコストがかかる可能性があります。文字の配列を作成join()し、最後にそれらをingすることは、Firefox、IE、およびSafariではかなり高速です(ただし、Chromeではかなり遅くなります):jsperf.com/tobase64-implementations
JLRishe

この関数を配列バッファーからbase64への変換に使用していますが、配列バッファーを元に戻すことができません。:私はここ_base64ToArrayBuffer()関数wrritenいるcodeshare.io/PT4pbをとしてではなく、それは私にエラーを与える:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal

これはこのJSZipでは機能しません。私は別の方法を見つけますgithub.com/michael/github/issues/137
RouR 2015

1
私はangualrjsとwebapi2を使用して50MBのPDFファイルをアップロードしようとしています。上記の行コードを使用していますが、ファイルをアップロードした後、ページがクラッシュしてハングしました。コードの行の下で、私は使用されましたが、webapiメソッドでnull値を取得しています。"var base64String = btoa(String.fromCharCode.apply(null、new Uint8Array(arrayBuffer))););" どんなアイデアでも提案してください...
prabhakaran S '14 / 11/16

102

これは私にとってはうまくいきます:

var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));

ES6では、構文は少し単純です。

let base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));

コメントで指摘されているように、このメソッドArrayBufferが大きい場合、一部のブラウザでランタイムエラーが発生する可能性があります。正確なサイズ制限は、どのような場合でも実装に依存します。


38
簡潔にするためにこの方法の方が好きですが、「最大コールスタックサイズ超過エラー」が発生します。上記のループ技術はそれを回避します。
Jay

13
スタックサイズエラーも発生しているので、mobzの回答を使用しました。
David Jones

12
大きなバッファでは機能しませんでした。機能させるためのわずかな変更:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex 2015年

1
私はangualrjsとwebapi2を使用して50MBのPDFファイルをアップロードしようとしています。上記の行コードを使用していますが、ファイルをアップロードした後、ページがクラッシュしてハングしました。コードの行の下で、私は使用されましたが、webapiメソッドでnull値を取得しています。"var base64String = btoa(String.fromCharCode.apply(null、new Uint8Array(arrayBuffer))););" どんなアイデアでも提案してください...
prabhakaran S '14 / 11/16

2
@Kugel btoaは、コード範囲0-255の文字に対して安全です。これが当てはまるためです(の8について考えてくださいUint8Array)。
GOTO 0

42

短いものが好きな人のために、Array.reduceスタックオーバーフローを引き起こさない他の使用方法があります:

var base64 = btoa(
  new Uint8Array(arrayBuffer)
    .reduce((data, byte) => data + String.fromCharCode(byte), '')
);

4
それが本当にセクシーかどうかわからない。結局のところ、<amount of Bytes in the buffer>新しい文字列を作成しています。
Neonit

いかがbtoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))ですか?
ロイティンカー

35

BlobとFileReaderを使用する別の非同期の方法があります。

パフォーマンスはテストしていません。しかし、それは別の考え方です。

function arrayBufferToBase64( buffer, callback ) {
    var blob = new Blob([buffer],{type:'application/octet-binary'});
    var reader = new FileReader();
    reader.onload = function(evt){
        var dataurl = evt.target.result;
        callback(dataurl.substr(dataurl.indexOf(',')+1));
    };
    reader.readAsDataURL(blob);
}

//example:
var buf = new Uint8Array([11,22,33]);
arrayBufferToBase64(buf, console.log.bind(console)); //"CxYh"

dataurl.split(',', 2)[1]代わりに使用しますdataurl.substr(dataurl.indexOf(',')+1)
カーターメドリン2018年

これは動作することが保証されていないようです。w3c.github.io/FileAPI/#issue-f80bda5bに よるとreadAsDataURL、理論的にはパーセントでエンコードされたdataURIを返す可能性があります(実際にはjsdomの場合にそうです
TS

@CarterMedlinなぜsplitより良いですsubstringか?
TS

分割は短いです。しかし、dataurlには1つ以上のコンマ(、)が含まれている場合があり、分割は安全ではありません。
cuixiping

12

私はこれを使用して私のために働いています。

function arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}



function base64ToArrayBuffer(base64) {
    var binary_string =  window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array( len );
    for (var i = 0; i < len; i++)        {
        bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes.buffer;
}

安全ではありません。@chemoishの回答を参照
Kugel

11

これに対する私の推奨は、ネイティブbtoa戦略を使用しないことです。なぜなら、それらはすべてを正しくエンコードしないからArrayBufferです…

DOM atob()およびbtoa()を書き換えます

DOMStringsは16ビットでエンコードされた文字列であるため、ほとんどのブラウザでは、文字が8ビットのASCIIエンコードされた文字の範囲を超えると、Unicode文字列でwindow.btoaを呼び出すと、文字の範囲外の例外が発生します。

この正確なエラーに遭遇したことはありませんが、ArrayBufferエンコードしようとしたの多くが正しくエンコードされていないことがわかりました。

MDN推奨または要旨を使用します。


btoaStringでは機能しませんが、OPはを求めていArrayBufferます。
tsh 2017年

1
これは非常に多く、間違ったことを推奨するスニペットがたくさんあります!私はこのエラーを何度も見ましたが、人々は盲目的にatobとbtoaを使用しています。
Kugel 2017

4
すべての配列バッファーは、他の回答の方法を使用して適切にエンコードする必要があります。atob/ btoaは、0xFF(定義上、バイト配列にはない)より大きい文字を含むテキストでのみ問題になります。他の回答で戦略を使用する場合、Uint8Arrayからの値は0から255の間であることが保証されるため、ASCII文字のみで構成される文字列が保証されるため、MDN警告は適用されません。これは、String.fromCharCodeが保証されることを意味します範囲外ではない文字を返します。
Jamesernator

11
var blob = new Blob([arrayBuffer])

var reader = new FileReader();
reader.onload = function(event){
   var base64 =   event.target.result
};

reader.readAsDataURL(blob);

1
回答に説明を追加してください。このコードはどういう意味ですか?
オスカー、


1
これは群を抜いて最速のアプローチです-私の限られたテストでは他のアプローチよりも数十倍高速です
jitin

私は再びこのソリューションを8時間ほど見つけたいと思います。私の日は無駄ではなかったでしょう;(ありがとう
カイザー

7

以下は、Uint8ArrayをBase64文字列に変換して再度戻すための2つの単純な関数です。

arrayToBase64String(a) {
    return btoa(String.fromCharCode(...a));
}

base64StringToArray(s) {
    let asciiString = atob(s);
    return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
}

1
これは混乱する答えです。これは有効なJavaScriptのようには見えず、Uint8ArrayはArrayBufferですか?
user1153660 2018年

@ user1153660 functionキーワードを追加すると、最新のブラウザで機能するはずです。
イエティ

驚くばかり!btoa(String.fromCharCode(... a)); Uint8Arrayをエンコードするためにこれまでに見た中で最も短いバージョンです。
ニコロ

1
これは見た目は良いですが、配列が大きすぎると、最大コールスタックサイズ超過エラーをスローします。
パヴェルPsztyć

0

ArrayBufferを使用して、から通常の配列を導出できますArray.prototype.slice。などの関数を使用して、Array.prototype.mapバイトを文字に変換し、joinそれらをまとめて文字列を形成します。

function arrayBufferToBase64(ab){

    var dView = new Uint8Array(ab);   //Get a byte view        

    var arr = Array.prototype.slice.call(dView); //Create a normal array        

    var arr1 = arr.map(function(item){        
      return String.fromCharCode(item);    //Convert
    });

    return window.btoa(arr1.join(''));   //Form a string

}

この方法では、文字列の連結が実行されていないため、より高速です。


安全ではありません。@chemoishの回答を参照
Kugel

0

OPはRunning Enviromentを指定していませんが、Node.JSを使用している場合、そのようなことを行う非常に簡単な方法があります。

公式のNode.JSドキュメントhttps://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodingsで Accordig

// This step is only necessary if you don't already have a Buffer Object
const buffer = Buffer.from(yourArrayBuffer);

const base64String = buffer.toString('base64');

また、たとえばAngularで実行している場合は、ブラウザクラスでもバッファクラスを使用できます。


あなたの答えはNodeJSにのみ適用され、ブラウザでは機能しません。
jvatic

1
@jvaticわかりました。OPは実行環境を明確に指定していなかったため、私の答えは間違っていませんJavascript。タグを付けただけです。だから私はそれをより簡潔にするために私の答えを更新しました。私はこれを行う方法を探していて、問題に対する最良の答えを得ることができなかったので、これは重要な答えだと思います。
ジョアン・エドゥアルド・ソアレス電子シルバ

-3

私の側では、Chromeナビゲーターを使用して、ArrayViewを読み取るためにDataView()を使用する必要がありました

function _arrayBufferToBase64( tabU8A ) {
var binary = '';
let lecteur_de_donnees = new DataView(tabU8A);
var len = lecteur_de_donnees.byteLength;
var chaine = '';
var pos1;
for (var i = 0; i < len; i++) {
    binary += String.fromCharCode( lecteur_de_donnees.getUint8( i ) );
}
chaine = window.btoa( binary )
return chaine;}

-4
function _arrayBufferToBase64(uarr) {
    var strings = [], chunksize = 0xffff;
    var len = uarr.length;

    for (var i = 0; i * chunksize < len; i++){
        strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));
    }

    return strings.join("");
}

これは、JSZipを使用して文字列からアーカイブを解凍する場合に適しています

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.