如何捕获FileReader base64作为变量?

时间:2017-11-09 06:18:50

标签: javascript base64 filereader

这会将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 demo of the above working code

我希望能够将base64分配给变量,所以我根据this answer尝试了以下内容:

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脚本环境中提交表单。

我之前已完成此操作,并将表单对象(包含文件)传递给Google Apps脚本功能。

但是,这种方法的一个限制是,如果将表单对象作为参数传递,则它是唯一允许的参数。

  

页面中的表单元素也是合法的参数,但它   必须是函数的唯一参数

Source

在这个例子中,我传递了多个参数,其中一个参数将是一个pdf文件,转换为base64。

为了回应@ Aasmund的好答案,我希望变量赋值可以阻止进一步的代码执行:

var my_pdf_file = [ converted file here ];

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

否则,我将不得不重构剩余的代码,发生在then块(由@Aasmund建议),由于验证/变量准备/条件处理的数量,这可能是混乱/不可能的在提交表单之前发生的事情。

2 个答案:

答案 0 :(得分:15)

FileReader.readAsDataURL() 异步 - 下载发生在后台,而其余代码继续执行。因此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 - 如果这变为真,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/626853中的讨论。如果您用户的浏览器足够现代,则可以使用async / await

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

(请注意,await仅适用于async个函数,因此您的点击处理程序必须为async。我还尝试添加繁忙的等待循环,但这导致我的浏览器挂起。)

答案 1 :(得分:0)

我按此守则工作

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