如何将FileReader的base64捕获为变量?

21

这会将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://dev59.com/G1oV5IYBdhLWcg3wauOP#36281449

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函数。

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

页面内的表单元素也可以作为参数,但它必须是函数的唯一参数

来源

在这种情况下,我正在通过多个参数传递,其中一个参数将是转换为base64的pdf文件。

针对@Aasmund的好答案,我希望变量赋值阻止进一步的代码执行:

var my_pdf_file = [ converted file here ];

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

否则,我将不得不重构剩余的代码,以便在then块中进行(由@Aasmund建议),但由于在提交表单之前进行了大量验证/变量准备/条件处理,这可能会很混乱/不可能。
2个回答

39

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

您可以选择重复检查reader.readyState === FileReader.DONE,例如在 setTimeout 回调函数或某些 DOM 事件处理程序中。只要这个条件成立,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://dev59.com/jnNA5IYBdhLWcg3wdtpd#39914235中的讨论。如果您的用户浏览器已足够现代,则可以使用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。我也尝试添加繁忙等待循环,但那会导致我的浏览器挂起。)

(另外,请注意来自 Charles Owen 的评论,readAsDataURL 实际上返回一个 data: URL,因此如果您只想获取 Base64 数据,则需要根据链接文档删除前缀。)


根据要求,已经在原帖中添加了上下文,谢谢。 - user1063287
@Zabs:我自己实际上还没有尝试过这种方法,但我相信在reader.readAsDataURL(file);之后执行以下代码应该可以工作: if (reader.readyState === FileReader.DONE) { console.log(reader.result); window.clearTimeout(timeoutID); } else { console.log("下载中..."); } }, 1000);``` (对于格式问题表示抱歉;似乎注释中无法使用代码块。) - Aasmund Eldhuset
如何解决“await is only valid in async function”问题? - Lei Yang
1
我非常喜欢这个 Promise 的例子,并且认为这是一种优雅的处理方式。我想补充一下,返回的结果不仅仅是图像的 base64 编码,而实际上是整个用于图像 src 的链接。 - Charles Owen
@CharlesOwen:谢谢您 - 我已经添加了那个。 - Aasmund Eldhuset
显示剩余3条评论

3
我会按照以下代码执行工作:
   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]);
        });
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接