Low-Level File Handling

Low-Level File Handling

Both PHP and Node.js modeled their core sets of file handling API functions on the original C language file handling API functions.

The PHP fopen() API function opens a file. The equivalent is the Node.js fs.open() API function. Both these API functions are modeled on the fopen() function from the

C programming language. A file can be opened in a variety of modes, including reading, writing, and appending.

The following PHP code demonstrates how to open the data.txt file for reading:

$fp = fopen ( 'data.txt' , 'r' );

In Node.js, the same data.txt file is opened using the Node.js fs.open() API function:

var fs = require ( 'fs' ); fs . open ( __dirname + '/' + 'data.txt' , 'r' , function ( err , fd ) {

// callback function });

To convert the PHP fopen() API function calls to Node.js, execute the following find- and-replace action:

Operation: "Find/Replace" in Eclipse PDT Find: fopen( Replace: fs.open( Options: None Action: Find, then Replace/Find

At each occurrence, apply the linearity concepts from previous chapters to correctly implement the Node.js callback function that replaces the PHP return value. Also, insert the __dirname constant before the first Node.js argument, if required.

In PHP, once a file is open, it can be read from using the PHP fread() API function. When the file is no longer needed, the PHP fclose() API function should be called on

the file. The following PHP code reads the first 1,000 bytes of a file. If the file is less than 1,000

bytes long, it reads the entire file:

$fp = fopen ( 'data.txt' , 'r' ); $contents = fread ( $fp , 1000 );

fclose ( $fp );

186 | Chapter 9: File Access

In Node.js, the Node.js fs.read() API function reads from a file. The Node.js fs.read() API function uses the buffer built-in module. Buffers hold an ordered col‐ lection of bytes.

Similar to PHP, the Node.js fs.close() API function closes a file. The following Node.js code reads the first 1,000 bytes of a file. If the file is less than 1,000

bytes long, it reads the entire file. The three lines of PHP code convert into 13 lines of Node.js code:

var fs = require ( 'fs' ); var Buffer = require ( 'buffer' ). Buffer ;

fs . open ( __dirname + '/' + 'data.txt' , 'r' , function ( err , fd ) { var contents = '' ; var raw = new Buffer ( 1000 ); fs . read ( fd , raw , 0 , raw . length , null , function ( err , bytesRead , buffer ) {

var buf = buffer . slice ( 0 , bytesRead );

contents += buf . toString ();

fs . close ( fd , function () {

console . log ( contents ); }); }); });

Besides the familiar callback functions needed in Node.js, the buffer variables, that is, the raw variable and the buf variable, add some complexity to the Node.js code. The raw variable is created to hold the data that read from the Node.js fs.read() API function. The buf variable is created to only contain the actual bytes read, essentially cutting off the unused bytes of the raw variable. The buffer argument is an alias for the raw variable; the callback assigns whatever buffer was passed as its second argument to the buffer parameter in its callback function.

In both PHP and Node.js, a file pointer is maintained that indicates the next bytes that will be read from the file. The end of the file can be tested using the PHP feof() API function. To read an entire file, the following PHP code uses the PHP feof() API func‐ tion to test for the end of the file:

$fp = fopen ( 'data.txt' , 'r' );

$contents = '' ; while ( ! feof ( $fp )) {

$contents .= fread ( $fp , 1000 );

} fclose ( $fp );

print $contents ;

Low-Level File Handling | 187

In Node.js, there is no equivalent to the PHP feof() API function. Instead, the bytes Read argument to the callback function is compared with the number of bytes requested that is passed as the fourth argument to the Node.js fs.read() API function. The eof variable in the following Node.js code illustrates where the equivalent PHP feof() API function call would go:

var fs = require ( 'fs' ); var Buffer = require ( 'buffer' ). Buffer ;

fs . open ( __dirname + '/' + 'data.txt' , 'r' , function ( err , fd ) { var contents = '' ; var raw = new Buffer ( 1000 );

var fread = function () {

fs . read ( fd , raw , 0 , raw . length , null , function ( err , bytesRead , buffer ) {

var eof = ( bytesRead != raw . length );

if ( ! eof ) {

contents += buffer . toString ();

fread (); } else {

if ( bytesRead > 0 ) { var buf = buffer . slice ( 0 , bytesRead ); contents += buf . toString ();

fs . close ( fd , function () {

console . log ( contents ); }); } }); }; fread (); });

Due to the nature of callbacks in Node.js, the fread function variable must be defined such that it can be called recursively if the file is longer than the raw buffer variable. The fread function variable is called continuously until the end of the file is reached. The fread function variable must be called explicitly to read the first buffer of data from the file, too.

At the end of the file, the partially filled buffer is processed, then the Node.js fs.close() API function is called. Using the linearity concepts, the Node.js console.log() API function call is placed in the callback of the Node.js fs.close() API function.

Besides the PHP fread() API function, there are also the PHP fgets() and fgetss() API functions. The PHP fgets() API function reads a single line from a file. The PHP fgetss() API function reads a single line from a file and removes all the PHP tags, HTML tags, and ASCII 0 (zero) bytes from the line.

Node.js does not have a corresponding API function to either the PHP fgets() or fgetss() API functions. With a little effort, it is possible to emulate these functions in Node.js, but instead, it is recommended that the PHP code be reexamined, and possibly,