【问题标题】:Read a file synchronously in Javascript在 Javascript 中同步读取文件
【发布时间】:2013-06-08 17:58:39
【问题描述】:

我想读取文件并使用 FileReader 对象将其转换为 base64 编码字符串。这是我使用的代码:

var reader = new FileReader(); reader.onloadend = 函数(evt){ // 文件被加载 result_base64 = evt.target.result; }; reader.readAsDataURL(文件);

但在这种情况下,我在事件处理程序(onLoadEnd 事件)中得到了转换的结果。我想要一个同步方法。有没有办法让“readAsDataURL”方法直接返回“result_base64”变量的值?

【问题讨论】:

  • “我想要一个同步方法。” 有什么特别的原因吗?我认为这是不可能的。
  • firefox 有/有 file.getAsDataURL() 方法,但它已被弃用,并且是任何浏览器的唯一同步版本,afaik。
  • 我将路径存储在本地存储数据库中,之后我需要将图像发送到服务器(所有图像都有一个循环,这就是我需要同步方法的原因)。我想避免在数据库中存储base64字符串,以免超过本地存储限制...
  • 但是您可以通过例如实现循环在上一张图片的完成回调中调用下一张的上传
  • 我想要一些更可重用的东西,可以将文件作为输入并返回 base64 编码的字符串作为输出,但这似乎是不可能的......谢谢你的帮助!

标签: javascript html filereader


【解决方案1】:

同步任务(阻塞)通常很糟糕。如果没有真正的理由同步执行此操作,我强烈建议您使用事件回调。

假设您的文件已损坏并且 HTML5 api 无法读取,它不会给您结果。它会破坏您的代码并阻止该站点。或者,有人可以选择一个 10GB 的文件,这会冻结您的 HTML 页面,直到文件完全加载。使用该异步事件处理程序,您可以捕获可能的错误。

为了解决回调的限制,我使用了一个简单的技巧:

var ready = false;
var result = '';

var check = function() {
    if (ready === true) {
         // do what you want with the result variable
         return;
    }
    setTimeout(check, 1000);
}

check();

var reader = new FileReader();
reader.onloadend = function(evt) {
    // file is loaded
    result = evt.target.result;
    
    ready = true;
};
reader.readAsDataURL(file);

检查功能,如果准备标志变量设置为真,则每秒检查一次。如果是这样,您可以确定结果是可用的。

这样做可能不是最佳实践,但我使用这种技术制作了一个 webapp 大约 30 次,同时运行了 10 多个 setTimeouts,直到现在都没有遇到任何问题。

【讨论】:

  • 遇到了与我想将参数变量传递给回调完全相同的问题。目的是将文件拆分为块,以便我可以将其逐块发送到服务器。一旦理解,闭包就是一件好事。
  • stackoverflow.com/a/46832928/851951 这种方法很好,但它仍然异步工作。
  • “同步任务(阻塞)通常很糟糕” - 错误。虽然对于常见的特定环境是正确的,例如与 Web 开发相关的环境,但它通常并不坏。 (例如,嵌入式硬件上的单线程非 Web、非 UI 专用应用程序)
  • 当然你是对的,但是由于这个问题是关于 javascripts html5 文件阅读器的,我们可以放心地假设我们在谈论浏览器环境。
  • 这不是同步任务。
【解决方案2】:

在 Node.js 中,使用 child_process 中的 execSync 并让 shell 为您同步读取它。将此子进程的输出重定向到父进程。

// Don't forget to use your favorite encoding in toString()
var execSync = require('child_process').execSync;
var fileContents = execSync('cat path/to/file.txt', {stdio: "pipe"}).toString();

我很乐意接受您对 UUOC 奖的提名。 ;)

【讨论】:

    【解决方案3】:

    要同步读取文件的内容,请使用fs.readFileSync

    var fs = require('fs');
    var content = fs.readFileSync('myfilename');
    console.log(content);
    

    fs.createReadStream 创建一个ReadStream

    【讨论】:

    • 这似乎是 node.JS 特有的,它不在任何浏览器中,因为任何其他人都被答案错误地定位了。
    • 非常感谢,这正是我需要的 nodejs 服务器 :)
    【解决方案4】:

    您可以使用标准的FileReaderSync,它是 FileReader API 的更简单、同步、阻塞的版本,类似于您已经在使用的:

    let reader = new FileReaderSync();
    let result_base64 = reader.readAsDataURL(file); 
    
    console.log(result_base64); // aGV5IHRoZXJl...
    

    请记住,出于显而易见的原因,这仅在工作线程中可用


    如果您需要一个“读起来像”同步 API(即顺序)的主线程解决方案,您可以将异步 FileReader 包装在一个 Promise 中并使用异步函数(您可能需要转译):

    async function readFileAsDataURL(file) {
        let result_base64 = await new Promise((resolve) => {
            let fileReader = new FileReader();
            fileReader.onload = (e) => resolve(fileReader.result);
            fileReader.readAsDataURL(file);
        });
    
        console.log(result_base64); // aGV5IHRoZXJl...
    
        return result_base64;
    }
    

    然后你可以在另一个异步上下文中等待这个函数:

    async function main() {
        let file = new File(...)
        let dataURL = await readFileAsDataURL(file)
        console.log(dataURL); // aGV5IHRoZXJl...
    }
    

    ... 或者只是使用 Promise 回调来使用它(不需要异步上下文):

    readFileAsDataURL(file).then(dataURL => {
        console.log(dataURL); // aGV5IHRoZXJl...
    });
    

    【讨论】:

    • 这个解决方案在美学上是更好的解决方案之一,但请记住 readFileAsDataURL 将进入事件循环,因此如果您从另一段同步代码中调用它,您还需要在那里等待它。否则它将返回待处理的承诺。
    • 是的,也许值得注意,如果你从其他函数调用它,它也应该是异步的,并用await关键字调用它,否则它只会返回“promise object”。
    • “FileReaderSync”在哪里定义?我收到错误
    • @sgowd 你试过网络工作者吗?它不适用于主线程。
    【解决方案5】:

    以下代码以同步方式读取文件

     function SyncFileReader(file) {
        let self = this;
        let ready = false;
        let result = '';
    
        const sleep = function (ms) {
          return new Promise(resolve => setTimeout(resolve, ms));
        }
    
        self.readAsArrayBuffer = async function() {
            while (ready === false) {
              await sleep(100);
            }
            return result;
        }    
    
        const reader = new FileReader();
        reader.onloadend = function(evt) {
            result = evt.target.result;
            ready = true;
        };
        reader.readAsArrayBuffer(file);
      }
    

    用法:

    const fileReader = new SyncFileReader(file);
    const arrayBuffer = await fileReader.readAsArrayBuffer();
    

    【讨论】:

    • 一个不错的答案,但你没有帮助他解决问题:convert it into a base64 encoded string。我也很乐意详细说明您为什么要执行每个步骤。
    • 如果换行 reader.readAsArrayBuffer(file);与 reader.readAsDataURL(file);在 SyncFileReader 然后它将文件的内容作为字符串返回
    • 您正在使用 await 关键字,因此您的代码不是同步的。
    【解决方案6】:

    以下代码以同步方式读取文件并将其内容作为文本(字符串)返回

     function SyncFileReader(file) {
        let self = this;
        let ready = false;
        let result = '';
    
        const sleep = function (ms) {
          return new Promise(resolve => setTimeout(resolve, ms));
        }
    
        self.readAsDataURL = async function() {
            while (ready === false) {
              await sleep(100);
            }
            return result;
        }    
    
        const reader = new FileReader();
        reader.onloadend = function(evt) {
            result = evt.target.result;
            ready = true;
        };
        reader.readAsDataURL(file);
      }
    

    用法:

    const fileReader = new SyncFileReader(file);
    const arrayBuffer = await fileReader.readAsDataURL();
    

    【讨论】:

    • 当你使用异步等待时,你怎么能说它可以同步呢?它从字面上说它的异步 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-25
    • 1970-01-01
    • 2021-11-18
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多