Write a data URI to a file in a Firefox extension

后端 未结 1 1209
予麋鹿
予麋鹿 2021-01-13 12:59

I am developing a Firefox addon. I need to save a bunch of data URI images to the disk. How do I approach to this?

I have browsed through the file I/O snippets on M

1条回答
  •  说谎
    说谎 (楼主)
    2021-01-13 13:18

    It sounds like you'd like to write not the data URI but the binary data it "contains", so I'll answer that.

    First, lets assume we got some actual data URI, (if not, adding data:application/octet-stream;base64, isn't too hard ;)

    // btoa("helloworld") as a placeholder ;)
    var imageDataURI = "data:application/octet-stream;base64,aGVsbG93b3JsZA==";
    

    Option 1 - Using OS.File

    OS.File has the benefit that it is truly async. On the other hand, NetUtil is only mostly async, in that there will be stat calls on the main thread and the file will be opened and potentially closed on the main thread as well (which can lead to buffer flushes and hence block the main thread while the flush is happening).

    After constructing a path (with some constants help), OS.File.writeAtomic is suited for the job.

    Components.utils.import("resource://gre/modules/osfile.jsm");
    
    var file = OS.Path.join(OS.Constants.Path.desktopDir, "test.png");
    
    var str = imageDataURI.replace(/^.*?;base64,/, "");
    // Decode to a byte string
    str = atob(str);
    // Decode to an Uint8Array, because OS.File.writeAtomic expects an ArrayBuffer(View).
    var data = new Uint8Array(str.length);
    for (var i = 0, e = str.length; i < e; ++i) {
      data[i] = str.charCodeAt(i);
    }
    
    // To support Firefox 24 and earlier, you'll need to provide a tmpPath. See MDN.
    // There is in my opinion no need to support these, as they are end-of-life and
    // contain known security issues. Let's not encourage users. ;)
    var promised = OS.File.writeAtomic(file, data);
    promised.then(
      function() {
        // Success!
      },
      function(ex) {
        // Failed. Error information in ex
      }
    );
    

    Option 2 - Using NetUtil

    NetUtil has some drawbacks in that is is not fully async, as already stated above.

    We can take a shortcut in that we can use NetUtil.asyncFetch to directly fetch the URL, which gives us a stream we can pass along to .asyncCopy.

    Components.utils.import("resource://gre/modules/NetUtil.jsm");
    Components.utils.import("resource://gre/modules/FileUtils.jsm");
    
    // file is nsIFile
    var file = FileUtils.getFile("Desk", ["test.png"]);
    
    NetUtil.asyncFetch(imageDataURI, function(inputstream, status) {
      if (!inputstream || !Components.isSuccessCode(status)) {
        // Failed to read data URI.
        // Handle error!
        return;
      }
    
      // You can also optionally pass a flags parameter here. It defaults to
      // FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE;
      var ostream = FileUtils.openSafeFileOutputStream(file);
    
      // The last argument (the callback) is optional.
      NetUtil.asyncCopy(inputstream , ostream, function(status) {
        if (!Components.isSuccessCode(status)) {
          // Handle error!
          return;
        }
    
        // Data has been written to the file.
      });
    });
    

    0 讨论(0)
提交回复
热议问题