Reading and Writing File Data
Problem
You want to read from or write to a locally stored file.
Solution
Node’s filesystem management functionality is included as part of the Node core, via
the fs module:
var fs = require('fs');
To read a file’s contents, use the readFile() function:
var fs = require('fs'); fs.readFile('main.txt', {encoding: 'utf8'},function(err,data) { if (err) { console.log("Error: Could not open file for reading\n"); } else { console.log(data); } });
To write to a file, use writeFile():
var fs = require('fs'); var buf = "I'm going to write this text to a file\n"; fs.writeFile('main2.txt', buf, function(err) { if (err) { console.log(err); } else { console.log("wrote text to file"); } });
The writeFile() function overwrites the existing file. To append text to the file, use appendText():
var fs = require('fs'); var buf = "I'm going to add this text to a file"; fs.appendFile('main2.txt', buf, function(err) { if (err) { console.log(err); } else { console.log("appended text to file"); } });
EXPLAIN
Node’s filesystem support is both comprehensive and simple to use. To read from a file,
use the readFile() function, which supports the following parameters:
• The filename, including the operating system path to the file if it isn’t local to the
application
• An options object, with options for encoding, as demonstrated in the solution, and
flag, which is set to r by default (for reading)
• A callback function with parameters for an error and the read data
In the solution, if I didn’t specify the encoding in my application, Node would have
returned the file contents as a raw buffer. Since I did specify the encoding, the file content
is returned as a string.
The writeFile() and appendFile() functions for writing and appending, respectively,
take parameters similar to readFile():
• The filename and path
• The string or buffer for the data to write to the file
• The options object, with options for encoding (w as default for writeFile() and
a as default for appendFile()) and mode, with a default value of 438 (0666 in Octal)
• The callback function, with only one parameter: the error
The options value of mode is used to set the file’s sticky and permission bits, if the file was
created because of the write or append.
By default, the file is created as readable and
writable by the owner, and readable by the group and the world.
I mentioned that the data to write can be either a buffer or a string.
A string cannot
handle binary data, so Node provides the Buffer, which is capable of dealing with either
strings or binary data. Both can be used in all of the filesystem functions discussed in
this section, but you’ll need to explicitly convert between the two types if you want to
use them both.
For example, instead of providing the utf8 encoding option when you use write
File(), you convert the string to a buffer, providing the desired encoding when you do:
var fs = require('fs'); var str = "I'm going to write this text to a file"; var buf = new Buffer(str, 'utf8'); fs.writeFile('mainbuf.txt', str, function(err) { if (err) { console.log(err); } else { console.log("wrote text to file"); } });
The reverse—that is, to convert the buffer to a string—is just as simple:
var fs = require('fs'); fs.readFile('main.txt', function(err,data) { if (err) { console.log(err.message); } else { var str = data.toString(); console.log(str); } });
The Buffer toString() function has three optional parameters: encoding, where to begin the conversion, and where to end it. By default, the entire buffer is converted using the utf8 encoding.
The readFile(), writeFile(), and appendFile() functions are asynchronous, mean‐ ing the won’t wait for the operation to finish before proceeding in the code. This is essential when it comes to notoriously slow operations such as file access.
There are synchronous versions of each: readFileSync(), writeFileSync(), and appendFile Sync(). I can’t stress enough that you should not use these variations. I only include a reference to them to be comprehensive.
Advanced
Another way to read or write from a file is to use the open() function in combination
with read() for reading the file contents, or write() for writing to the file. The advan‐
tages to this approach is more finite control of what happens during the process.
The
disadvantage is the added complexity associated with all of the functions, including only
being able to use a buffer for reading from and writing to the file.
The parameters for open() are:
• Filename and path
• Flag
• Optional mode
• Callback function
The same open() is used with all operations, with the flag controlling what happens.
There are quite a few flag options, but the ones that interest us the most at this time are:
• r: Opens the file for reading; the file must exist
• r+: Opens the file for reading and writing; an exception occurs if the file doesn’t
exist
• w: Opens the file for writing, truncates the file, or creates it if it doesn’t exist
• wx: Opens the file for writing, but fails if the file does exist
• w+: Opens the file for reading and writing; creates the file if it doesn’t exist; truncates
the file if it exists
• wx+: Similar to w+, but fails if the file exists
• a: Opens the file for appending, creates it if it doesn’t exist
• ax: Opens the file for appending, fails if the file exists
• a+: Opens the file for reading and appending; creates the file if it doesn’t exist
• ax+: Similar to a+, but fails if the file exists
The mode is the same one mentioned earlier, a value that sets the sticky and permis‐
sion bits on the file if created, and defaults to 0666. The callback function has two pa‐
rameters: an error object, if an error occurs, and a file descriptor, used by subsequent
file operations.
The read() and write() functions share the same basic types of parameters:
• The open() methods callback file descriptor
• The buffer used to either hold data to be written or appended, or read
• The offset where the input/output (I/O) operation begins
• The buffer length (set by read operation, controls write operation)
• Position in the file where the operation is to take place; null if the position is the
current position
The callback functions for both methods have three arguments: an error, bytes read (or
written), and the buffer.
That’s a lot of parameters and options.
The best way to demonstrate how it all works is
to create a complete Node application that opens a brand new file for writing, writes
some text to it, writes some more text to it, and then reads all the text back and prints
it to the console. Since open() is asynchronous, the read and write operations have to
occur within the callback function. Be ready for it , because you’re going
to get your first taste of a concept known as callback hell.
Demonstrating open, read, and write
var fs = require('fs'); fs.open('newfile.txt', 'a+',function(err,fd){ if (err) { console.log(err.message); } else { var buf = new Buffer("The first string\n"); fs.write(fd, buf, 0, buf.length, 0, function(err, written, buffer) { if (err) { console.log(err.message); } else { var buf2 = new Buffer("The second string\n"); fs.write(fd, buf2, 0, buf2.length, 0, function(err, written2, buffer) { if (err) { console.log(err.message); } else { var length = written + written2; var buf3 = new Buffer(length); fs.read(fd, buf3, 0, length, 0, function( err, bytes, buffer) { if(err) { console.log(err.message); } else { console.log(buf3.toString()); } }); } }); } }); } });
To find the length of the buffers, I used length, which returns the number of bytes for the buffer. This value doesn’t necessarily match the length of a string in the buffer, but it does work in this usage. That many levels of indentation can make your skin crawl, but the example demonstrates how open(), read(), and write() work. These combinations of functions are what’s used within the readFile(), writeFile(), and appendFile() functions to manage file access. The higher level functions just simplify the most common file operations.
No comments:
Post a Comment