web-dev-qa-db-ja.com

FileReader base64を変数としてキャプチャする方法は?

これにより、base64がコンソールに出力されます。

function getBase64(file) {
    var reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = function() {
        console.log(reader.result);
    };
    reader.onerror = function(error) {
        console.log('Error: ', error);
    };
}

var file = document.querySelector('#files > input[type="file"]').files[0];
getBase64(file); // prints the base64 string

ソース:https://stackoverflow.com/a/36281449/1063287

jsFiddle:上記の作業コードのjsFiddleデモ

Base64を変数に割り当てることができるようにしたいので、- この答え に基づいて以下を試しました:

function getBase64(file, onLoadCallback) {
    var reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = onLoadCallback;
    reader.onerror = function(error) {
        console.log('Error when converting PDF file to base64: ', error);
    };
}

var my_pdf_file = document.querySelector("#my_pdf_file").files[0];
var my_pdf_file_as_base64 = "";
getBase64(my_pdf_file, function(e) {
    my_pdf_file_as_base64 = e.target.result
});

// print out the base64 to show i have captured the value correctly
console.log(my_pdf_file_as_base64);

現在、コンソールに何も出力していません。

質問:

どうすればbase64値を変数として保存できますか?

編集:

要求に応じて、コンテキスト:

Google Apps Script環境でフォームを送信しています。

私はこれを以前に行い、フォームオブジェクト(ファイルを含む)をGoogle Apps Script関数に渡しました。

ただし、このアプローチの制約の1つは、フォームオブジェクトをパラメーターとして渡す場合、それが許可される唯一のパラメーターであることです。

ページ内のフォーム要素もパラメータとして有効ですが、関数の唯一のパラメータである必要があります

ソース

この例では、複数のパラメーターを渡します。パラメーターの1つは、base64に変換されたpdfファイルになります。

@Aasmundの素晴らしい答えに応えて、変数の割り当てによってそれ以上のコード実行をブロックしたいと思います。

var my_pdf_file = [ converted file here ];

// don't do this stuff until the above variable is assigned

そうでない場合は、残りのコードをthenブロックで実行するようにリファクタリングする必要があります(@Aasmundによって提案されます)。フォームが送信される前に行われます。

10
user1063287

FileReader.readAsDataURL() isasynchronous-ダウンロードはバックグラウンドで行われ、残りのコードは実行され続けます。したがって、console.log(my_pdf_file_as_base64);が空の文字列を出力する理由は、_my_pdf_file_as_base64 = e.target.result_の行がまだ実行されていないためです:getBase64()の呼び出しはほとんどすぐに終了し、後続のステートメントが実行されます;後で(ダウンロードが完了したときに)コールバックが実行されます。

これを処理する方法は、ダウンロードしたファイルを使用するコードをコールバックの内部に配置することです。

_getBase64(my_pdf_file, function(e) {
    my_pdf_file_as_base64 = e.target.result;
    console.log(my_pdf_file_as_base64);
});
_

または、繰り返し(たとえば、setTimeoutコールバック内または一部のDOMイベントハンドラー内)、_reader.readyState === FileReader.DONE_-これがtrueになるたびに、_reader.result_にファイルが含まれるかどうかを確認できます。

より柔軟なアプローチは、非同期計算をカプセル化するオブジェクトである Promise を使用することです。

_function getBase64(file, onLoadCallback) {
    return new Promise(function(resolve, reject) {
        var reader = new FileReader();
        reader.onload = function() { resolve(reader.result); };
        reader.onerror = reject;
        reader.readAsDataURL(file);
    });
}

var promise = getBase64(my_pdf_file);
promise.then(function(result) {
    console.log(result);
});
_

これまでのところ、これは最初のソリューションと非常に似ていますが、promiseが他の関数に渡すことができるオブジェクトであるため、ある場所で計算を開始し、別の場所で何が起こるかを決定できるという利点があります終了しました。

おそらくお気づきのように、これらのアプローチはどちらも、ファイルのコンテンツがグローバル変数_my_pdf_file_as_base64_に割り当てられるまで、それ以上のコード実行をブロックすることを許可しません。これは仕様によるものです。ただし、古いコードをリファクタリングする時間がないために本当にダウンロードをブロックする必要がある場合は、 https://stackoverflow.com/a/39914235/62685 の説明を参照してください。ユーザーのブラウザーが十分に最新である場合は、 async / await を使用できます。

_$(document).on("click", ".clicker", async function() {
    var promise = getBase64(my_pdf_file);
    var my_pdf_file_as_base64 = await promise;
}
_

awaitasync関数内でのみ機能するため、クリックハンドラーはasyncでなければなりません。ビジー待機ループを追加することも試みましたが、ブラウザーがハングしました。)

21

私はこの規範に従って働きます

   async function checkFileBase64(el) {
        let inputEl = $(el).find('input[type=file]');
        let promise = getBase64(inputEl[0]);
        return await promise;
    }
    function getBase64(file) {
       return new Promise(function (resolve, reject) {
            let reader = new FileReader();
            reader.onload = function () { resolve(reader.result); };
            reader.onerror = reject;
            reader.readAsDataURL(file.files[0]);
        });
    }
0
Milad Jafari