MozillaZine

How to read and write to files?

Talk about add-ons and extension development.
lightnb
 
Posts: 103
Joined: August 31st, 2005, 11:21 pm

Post Posted January 23rd, 2014, 2:37 am

From an extension, you can supposedly read and write data to regular system files, but I'm not sure of the right API to use to do that.

I found the nsIScriptableIO (https://developer.mozilla.org/en-US/doc ... riptableIO), which looks like it would do what I want, but it also has a note saying it was never actually implemented!

I also found File I/O (https://developer.mozilla.org/en-US/Add ... 2FFile_I_O) and nsIIOService (https://developer.mozilla.org/en-US/doc ... eURI%28%29).

There are also many other similarly named APIs too, so I'm confused by all of the options, and not sure what is the simplest way to read and write to text files from an add-on.

Specifically, I want to save a JSON string to a file and read it back later. Which is the right API to use?

lithopsian
 
Posts: 3664
Joined: September 15th, 2010, 9:03 am

Post Posted January 23rd, 2014, 5:25 am

nsIFile is the base interface for dealing with files, but it doesn't directly provide functions for reading and writing to them. For that you would typically use nsIFileInputStream and nsIFileOutputStream. You would probably start from nsILocalFile if you're dealing with a normal file on your local file system.

For files which are known as a URI, for example files inside jar or xpi archives, or remote files, you can use XMLHttpRequest to load the file. This contains a number of convenient methods for parsing file formats such as XML and JSON, so you will sometimes use it for reading local files.

lightnb
 
Posts: 103
Joined: August 31st, 2005, 11:21 pm

Post Posted January 23rd, 2014, 7:42 am

I think I understand what to do now, but I'm not sure how to include nsIFileInputStream.

Code: Select all
GetFileContents = function(FileName){
      Components.utils.import("resource://gre/modules/FileUtils.jsm");
      var file = new FileUtils.File(FileName);
      return nsIFileInputStream.init(file,-1,-1);
}


Shouldn't there be a " Components.utils.import" line for nsIFileInputStream? How do I know what that is?

lithopsian
 
Posts: 3664
Joined: September 15th, 2010, 9:03 am

Post Posted January 23rd, 2014, 8:33 am

Examples:
Code: Select all
   readFile : function(file)
   {
      var response = "";
      try
      {
         if (file.exists() && file.isReadable())
         {
            var fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
            var sstream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
            fstream.init(file, -1, 0, 0);
            sstream.init(fstream);
            var str = sstream.read(4096);
            while (str.length > 0)
            {
               response += str;
               str = sstream.read(4096);
            }
            sstream.close();
            fstream.close();
         }
      }
      catch(e) {};
      return response;
   },

   writeFile : function(file, data)
   {
      try
      {
         var stream = Cc["@mozilla.org/network/safe-file-output-stream;1"].createInstance(Ci.nsISafeOutputStream);
         stream.QueryInterface(Ci.nsIFileOutputStream).init(file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate, rw-rw-rw-
         stream.write(data, data.length);
         stream.finish();
      }
      catch(e) {};
   },


Scriptable input streams are used from javascript to access input streams because the read functions on nsIInputStream interface itself are not callable from javascript.

Safe output streams are a variation on file output streams that write to a copy of the file and then replace the original file in its entirety once the stream has been closed and file file safely written. This avoids file corruption in the event of something bad happeing halfway through the stream.

lightnb
 
Posts: 103
Joined: August 31st, 2005, 11:21 pm

Post Posted January 25th, 2014, 5:07 am

Thanks, I was able to use the code you posted and it's working now.

Noitidart
 
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Post Posted January 25th, 2014, 10:38 am

I think litho your example is synchronous right?



Here is what I use, its async. Here is copy paste example for scratchpad in "Browser" environment.

Code: Select all
/*start - example usage*/
var file = FileUtils.getFile("Desk", ["rawr.txt"]); //this gets file on desktop named 'rawr.txt'
//can also go: var file = FileUtils.File('C:\\Documents and Settings\\My User Name\\Desktop\\rawr.txt');

overwriteFile(file, 'blah blah blah', function (status) {
   alert('overwrite status == ' + Components.isSuccessCode(status));
});

readFile(file, function (dataReadFromFile, status) {
   alert('file read status == ' + Components.isSuccessCode(status));
   alert('contents of file is:\n' + dataReadFromFile);
});
/*end - example usage*/





Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
function overwriteFile(nsiFile, data, callback) {
   //data is data you want to write to file
   //if file doesnt exist it is created
   var ostream = FileUtils.openSafeFileOutputStream(file)
   var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   var istream = converter.convertToInputStream(data);
   // The last argument (the callback) is optional.
   NetUtil.asyncCopy(istream, ostream, function (status) {
      if (!Components.isSuccessCode(status)) {
         // Handle error!
         alert('error on write isSuccessCode = ' + status);
         return;
      }
      // Data has been written to the file.
      callback(status)
   });
}

function readFile(nsiFile, callback) {
   //you must pass a callback like function(dataReadFromFile, status) { }
   //then within the callback you can work with the contents of the file, it is held in dataReadFromFile
   //callback gets passed the data as string
   NetUtil.asyncFetch(file, function (inputStream, status) {
      //this function is callback that runs on completion of data reading
      if (!Components.isSuccessCode(status)) {
         alert('error on file read isSuccessCode = ' + status);
         return;
      }
      var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
      callback(data, status);
   });
}

lithopsian
 
Posts: 3664
Joined: September 15th, 2010, 9:03 am

Post Posted January 25th, 2014, 1:34 pm

Async is better. Most of the time I don't bother with NetUtils because XMLHttpRequest will do async reads with much more flexibility.

Noitidart
 
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Post Posted January 31st, 2014, 3:02 am

hey litho, im having an issue. im trying to help babylon with some stuff. so they have these content script files and image files they want to inject into a website. so i tried
Code: Select all
<img src="jar......">

but thats rejected due to security reasons.

so im trying to read the file and store the data in a string like 'data:image/png' or 'data:text/js' and then inject it into web page like this:
Code: Select all
<img src='data:image/png,...'>
<script src="data:text/js,....">

where ... is the read data



so the issue:
i'm trying to read a file from addon. its location is this:
Code: Select all
jar:file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/babylon-spoonfeed@jetpack.xpi!/Praxis.js


if i paste this into the url bar it loads fine, but how to make a nsiFile out of this so i can read it?

im running into the problem here:
Code: Select all
FileUtils.File('jar:file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/babylon-spoonfeed@jetpack.xpi!/Praxis.js');




my second question is. can you please help me on how to use FileRead api for this:
https://developer.mozilla.org/en-US/doc ... FileReader

lithopsian
 
Posts: 3664
Joined: September 15th, 2010, 9:03 am

Post Posted January 31st, 2014, 3:49 am

Read jar files using XMLHttpRequest. They aren't real files, or at least the contents of them aren't, but XMLHttpRequest will accept the url that represents a file inside a jar and send you its contents. It will also automatically parse JSON, XML, and various other things.

I've never used the FileReader API. I think you can only read files that the user has selected manually (and certain other pseudo-files), for security reasons.

Noitidart
 
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Post Posted January 31st, 2014, 4:06 am

much thanks for super fast reply litho! ill look into xmlhttprequest :)

Noitidart
 
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Post Posted January 31st, 2014, 4:28 am

hey litho can you plz share some xmlhttprequest reader with me :( its 330am and im tired haha :P i wanted to just sleep right now and magically have code after work tomorrow haha. if ur busy or something i understand :)

max1million
 
Posts: 2810
Joined: November 15th, 2004, 5:03 am

Post Posted January 31st, 2014, 6:08 am

convert the image to a data:image/png;base64, in plain text file in extension can read text to set var with
Code: Select all
var url = "https://developer.mozilla.org/en/DOM/XMLHttpRequest"; // chrome: file: etc also work
// var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].getService(Ci.nsIXMLHttpRequest);
var req = new XMLHttpRequest();
req.onload = function onload(e) {
  alert(req.responseText);
};
req.open("GET", url, true);  //(method, url, async)
req.send(null);

Noitidart
 
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Post Posted January 31st, 2014, 10:37 am

Much thanks max and litho! :)

Noitidart
 
Posts: 1168
Joined: September 16th, 2007, 8:01 am

Post Posted January 31st, 2014, 11:34 am

oh my gosh i guys i just learned that i can still use my readFile function from above (quoted below).

i thought my funciton only took nsiFile however it uses NetUtil.asyncFetch:
https://developer.mozilla.org/en-US/doc ... etch%28%29

which accepts for aSource parameter:
The source to open asynchronously. This may be specified as an nsIURI, nsIFile, nsIChannel, nsIInputStream or as a string specifying the URL to open.


This is fantastic! I just learned a lot here.

so this is my code to base64 anything:
Code: Select all
Components.utils.import("resource://gre/modules/NetUtil.jsm");
readFile('file:///C:/Documents%20and%20Settings/SONY%20VAIO/My%20Documents/GitHub/babylon/subfolder/Button.png', function (dataReadFromFile, status) {
   if (!Components.isSuccessCode(status)) {
      Services.wm.getMostRecentWindow('navigator:browser').alert('error');
   } else {
      Services.wm.getMostRecentWindow('navigator:browser').alert('success ' + dataReadFromFile);
      Services.wm.getMostRecentWindow('navigator:browser').gBrowser.contentDocument.documentElement.innerHTML = '<img src="data:image;base64,' + Services.appShell.hiddenDOMWindow.btoa(dataReadFromFile) + '">';
   }
});


if i want to insert js file simply change
Code: Select all
Services.wm.getMostRecentWindow('navigator:browser').gBrowser.contentDocument.documentElement.innerHTML = '<script src="data:image;base64,' + Services.appShell.hiddenDOMWindow.btoa(dataReadFromFile) + '"></script>';


readFile from above
Code: Select all
function readFile(nsiFile, callback) {
   //you must pass a callback like function(dataReadFromFile, status) { }
   //then within the callback you can work with the contents of the file, it is held in dataReadFromFile
   //callback gets passed the data as string
   NetUtil.asyncFetch(nsiFile, function (inputStream, status) {
      //this function is callback that runs on completion of data reading
      if (!Components.isSuccessCode(status)) {
         //alert('error on file read isSuccessCode = ' + status);
         callback(null, status);
         return;
      }
      var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
      callback(data, status);
   });
}

TCaudillLG
 
Posts: 35
Joined: March 2nd, 2005, 4:27 pm
Location: Middletown, OH

Post Posted January 31st, 2014, 11:45 pm

You think you've learned something from this thread, but trust me... when it comes time to actually implement this in your addon, you'll find you haven't learned very much. Biggest waste of time ever, the equivalent of trying to build a house on a swamp.

Return to Extension Development


Who is online

Users browsing this forum: No registered users and 3 guests