Skip to content
This repository
Browse code

add positioned file writing feature to fs.WriteStream

Patterned on same feature in ReadStream; a small bit of new code added
plus two refactorings of previous code; added two test files.
  • Loading branch information...
commit ca35782f5b704b2262c2d897aa645b5267dce693 1 parent f4c38cc
tshinnic authored
5  doc/api/fs.markdown
Source Rendered
@@ -493,3 +493,8 @@ Returns a new WriteStream object (See `Writable Stream`).
493 493
     { flags: 'w',
494 494
       encoding: null,
495 495
       mode: 0666 }
  496
+
  497
+`options` may also include a `start` option to allow writing data at 
  498
+some position past the beginning of the file.  Modifying a file rather 
  499
+than replacing it may require a `flags` mode of `r+` rather than the 
  500
+default mode `w`.
42  lib/fs.js
@@ -1109,6 +1109,14 @@ var WriteStream = fs.WriteStream = function(path, options) {
1109 1109
     this[key] = options[key];
1110 1110
   }
1111 1111
 
  1112
+  if (this.start !== undefined) {
  1113
+    if (this.start < 0) {
  1114
+      throw new Error('start must be >= zero');
  1115
+    }
  1116
+
  1117
+    this.pos = this.start;
  1118
+  }
  1119
+
1112 1120
   this.busy = false;
1113 1121
   this._queue = [];
1114 1122
 
@@ -1150,10 +1158,18 @@ WriteStream.prototype.flush = function() {
1150 1158
 
1151 1159
     if (method == fs.write) {
1152 1160
       self.bytesWritten += arguments[1];
1153  
-    }
  1161
+      if (cb) {
  1162
+        // write callback
  1163
+        cb(null, arguments[1]);
  1164
+      }
1154 1165
 
1155  
-    // stop flushing after close
1156  
-    if (method === fs.close) {
  1166
+    } else if (method === fs.open) {
  1167
+      // save reference for file pointer
  1168
+      self.fd = arguments[1];
  1169
+      self.emit('open', self.fd);
  1170
+
  1171
+    } else if (method === fs.close) {
  1172
+      // stop flushing after close
1157 1173
       if (cb) {
1158 1174
         cb(null);
1159 1175
       }
@@ -1161,15 +1177,6 @@ WriteStream.prototype.flush = function() {
1161 1177
       return;
1162 1178
     }
1163 1179
 
1164  
-    // save reference for file pointer
1165  
-    if (method === fs.open) {
1166  
-      self.fd = arguments[1];
1167  
-      self.emit('open', self.fd);
1168  
-    } else if (cb) {
1169  
-      // write callback
1170  
-      cb(null, arguments[1]);
1171  
-    }
1172  
-
1173 1180
     self.flush();
1174 1181
   });
1175 1182
 
@@ -1194,14 +1201,17 @@ WriteStream.prototype.write = function(data) {
1194 1201
     cb = arguments[arguments.length - 1];
1195 1202
   }
1196 1203
 
1197  
-  if (Buffer.isBuffer(data)) {
1198  
-    this._queue.push([fs.write, data, 0, data.length, null, cb]);
1199  
-  } else {
  1204
+  if (!Buffer.isBuffer(data)) {
1200 1205
     var encoding = 'utf8';
1201 1206
     if (typeof(arguments[1]) == 'string') encoding = arguments[1];
1202  
-    this._queue.push([fs.write, data, undefined, encoding, cb]);
  1207
+    data = new Buffer('' + data, encoding);
1203 1208
   }
1204 1209
 
  1210
+  this._queue.push([fs.write, data, 0, data.length, this.pos, cb]);
  1211
+
  1212
+  if (this.pos !== undefined) {
  1213
+    this.pos += data.length;
  1214
+  }
1205 1215
 
1206 1216
   this.flush();
1207 1217
 
98  test/simple/test-file-write-stream2.js
... ...
@@ -0,0 +1,98 @@
  1
+// Copyright Joyent, Inc. and other Node contributors.
  2
+//
  3
+// Permission is hereby granted, free of charge, to any person obtaining a
  4
+// copy of this software and associated documentation files (the
  5
+// "Software"), to deal in the Software without restriction, including
  6
+// without limitation the rights to use, copy, modify, merge, publish,
  7
+// distribute, sublicense, and/or sell copies of the Software, and to permit
  8
+// persons to whom the Software is furnished to do so, subject to the
  9
+// following conditions:
  10
+//
  11
+// The above copyright notice and this permission notice shall be included
  12
+// in all copies or substantial portions of the Software.
  13
+//
  14
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  15
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  17
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  18
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  19
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  20
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
  21
+
  22
+var common = require('../common');
  23
+var assert = require('assert');
  24
+
  25
+var path = require('path'),
  26
+    fs = require('fs'),
  27
+    util = require('util');
  28
+
  29
+
  30
+var filepath = path.join(common.tmpDir, 'write.txt'),
  31
+    file;
  32
+
  33
+var EXPECTED = '012345678910';
  34
+
  35
+var cb_expected = 'write open drain write drain close error ',
  36
+    cb_occurred = '';
  37
+
  38
+var countDrains = 0;
  39
+
  40
+
  41
+process.on('exit', function() {
  42
+  removeTestFile();
  43
+  if ( cb_occurred !== cb_expected) {
  44
+    console.log('  Test callback events missing or out of order:');
  45
+    console.log('    expected: %j', cb_expected);
  46
+    console.log('    occurred: %j', cb_occurred);
  47
+    assert.strictEqual(cb_occurred, cb_expected,
  48
+          'events missing or out of order: "' +
  49
+          cb_occurred + '" !== "' + cb_expected + '"');
  50
+  }
  51
+});
  52
+
  53
+function removeTestFile() {
  54
+  try {
  55
+    fs.unlinkSync(filepath);
  56
+  } catch (e) {}
  57
+}
  58
+
  59
+
  60
+removeTestFile();
  61
+
  62
+file = fs.createWriteStream(filepath);
  63
+
  64
+file.on('open', function(fd) {
  65
+  cb_occurred += 'open ';
  66
+  assert.equal(typeof fd, 'number');
  67
+});
  68
+
  69
+file.on('drain', function() {
  70
+  cb_occurred += 'drain ';
  71
+  ++countDrains;
  72
+  if (countDrains === 1) {
  73
+    assert.equal(fs.readFileSync(filepath), EXPECTED);
  74
+    file.write(EXPECTED);
  75
+    cb_occurred += 'write ';
  76
+  } else if (countDrains == 2) {
  77
+    assert.equal(fs.readFileSync(filepath), EXPECTED + EXPECTED);
  78
+    file.end();
  79
+  }
  80
+});
  81
+
  82
+file.on('close', function() {
  83
+  cb_occurred += 'close ';
  84
+  assert.strictEqual(file.bytesWritten, EXPECTED.length * 2);
  85
+  file.write('should not work anymore');
  86
+});
  87
+
  88
+
  89
+file.on('error', function(err) {
  90
+  cb_occurred += 'error ';
  91
+  assert.ok(err.message.indexOf('not writable') >= 0);
  92
+});
  93
+
  94
+
  95
+for (var i = 0; i < 11; i++) {
  96
+  assert.strictEqual(file.write(i), false);
  97
+}
  98
+cb_occurred += 'write ';
187  test/simple/test-file-write-stream3.js
... ...
@@ -0,0 +1,187 @@
  1
+
  2
+var common = require('../common');
  3
+var assert = require('assert');
  4
+
  5
+var path = require('path'),
  6
+    fs = require('fs'),
  7
+    util = require('util');
  8
+
  9
+
  10
+var filepath = path.join(common.tmpDir, 'write_pos.txt');
  11
+
  12
+
  13
+var cb_expected = 'write open close write open close write open close ',
  14
+    cb_occurred = '';
  15
+
  16
+var fileDataInitial = 'abcdefghijklmnopqrstuvwxyz';
  17
+
  18
+var fileDataExpected_1 = 'abcdefghijklmnopqrstuvwxyz';
  19
+var fileDataExpected_2 = 'abcdefghij123456qrstuvwxyz';
  20
+var fileDataExpected_3 = 'abcdefghij\u2026\u2026qrstuvwxyz';
  21
+
  22
+
  23
+process.on('exit', function() {
  24
+  removeTestFile();
  25
+  if ( cb_occurred !== cb_expected) {
  26
+    console.log('  Test callback events missing or out of order:');
  27
+    console.log('    expected: %j', cb_expected);
  28
+    console.log('    occurred: %j', cb_occurred);
  29
+    assert.strictEqual(cb_occurred, cb_expected,
  30
+          'events missing or out of order: "' +
  31
+          cb_occurred + '" !== "' + cb_expected + '"');
  32
+  }
  33
+});
  34
+
  35
+function removeTestFile() {
  36
+  try {
  37
+    fs.unlinkSync(filepath);
  38
+  } catch (ex) { }
  39
+}
  40
+
  41
+
  42
+removeTestFile();
  43
+
  44
+
  45
+function run_test_1() {
  46
+  var file, buffer, options;
  47
+
  48
+  options = {};
  49
+  file = fs.createWriteStream(filepath, options);
  50
+  console.log('    (debug: start         ', file.start);
  51
+  console.log('    (debug: pos           ', file.pos);
  52
+
  53
+  file.on('open', function(fd) {
  54
+    cb_occurred += 'open ';
  55
+  });
  56
+
  57
+  file.on('close', function() {
  58
+    cb_occurred += 'close ';
  59
+    console.log('    (debug: bytesWritten  ', file.bytesWritten);
  60
+    console.log('    (debug: start         ', file.start);
  61
+    console.log('    (debug: pos           ', file.pos);
  62
+    assert.strictEqual(file.bytesWritten, buffer.length);
  63
+    var fileData = fs.readFileSync(filepath, 'utf8');
  64
+    console.log('    (debug: file data   ', fileData);
  65
+    console.log('    (debug: expected    ', fileDataExpected_1);
  66
+    assert.equal(fileData, fileDataExpected_1);
  67
+
  68
+    run_test_2();
  69
+  });
  70
+
  71
+  file.on('error', function(err) {
  72
+    cb_occurred += 'error ';
  73
+    console.log('    (debug: err event ', err);
  74
+    throw err;
  75
+  });
  76
+
  77
+  buffer = new Buffer(fileDataInitial);
  78
+  file.write(buffer);
  79
+  cb_occurred += 'write ';
  80
+
  81
+  file.end();
  82
+}
  83
+
  84
+
  85
+function run_test_2() {
  86
+  var file, buffer, options;
  87
+
  88
+  buffer = new Buffer('123456');
  89
+
  90
+  options = { start: 10,
  91
+              flags: 'r+' };
  92
+  file = fs.createWriteStream(filepath, options);
  93
+  console.log('    (debug: start         ', file.start);
  94
+  console.log('    (debug: pos           ', file.pos);
  95
+
  96
+  file.on('open', function(fd) {
  97
+    cb_occurred += 'open ';
  98
+  });
  99
+
  100
+  file.on('close', function() {
  101
+    cb_occurred += 'close ';
  102
+    console.log('    (debug: bytesWritten  ', file.bytesWritten);
  103
+    console.log('    (debug: start         ', file.start);
  104
+    console.log('    (debug: pos           ', file.pos);
  105
+    assert.strictEqual(file.bytesWritten, buffer.length);
  106
+    var fileData = fs.readFileSync(filepath, 'utf8');
  107
+    console.log('    (debug: file data   ', fileData);
  108
+    console.log('    (debug: expected    ', fileDataExpected_2);
  109
+    assert.equal(fileData, fileDataExpected_2);
  110
+
  111
+    run_test_3();
  112
+  });
  113
+
  114
+  file.on('error', function(err) {
  115
+    cb_occurred += 'error ';
  116
+    console.log('    (debug: err event ', err);
  117
+    throw err;
  118
+  });
  119
+
  120
+  file.write(buffer);
  121
+  cb_occurred += 'write ';
  122
+
  123
+  file.end();
  124
+}
  125
+
  126
+
  127
+function run_test_3() {
  128
+  var file, buffer, options;
  129
+
  130
+  var data = '\u2026\u2026',    // 3 bytes * 2 = 6 bytes in UTF-8
  131
+      fileData;
  132
+
  133
+  options = { start: 10,
  134
+              flags: 'r+' };
  135
+  file = fs.createWriteStream(filepath, options);
  136
+  console.log('    (debug: start         ', file.start);
  137
+  console.log('    (debug: pos           ', file.pos);
  138
+
  139
+  file.on('open', function(fd) {
  140
+    cb_occurred += 'open ';
  141
+  });
  142
+
  143
+  file.on('close', function() {
  144
+    cb_occurred += 'close ';
  145
+    console.log('    (debug: bytesWritten  ', file.bytesWritten);
  146
+    console.log('    (debug: start         ', file.start);
  147
+    console.log('    (debug: pos           ', file.pos);
  148
+    assert.strictEqual(file.bytesWritten, data.length * 3);
  149
+    fileData = fs.readFileSync(filepath, 'utf8');
  150
+    console.log('    (debug: file data   ', fileData);
  151
+    console.log('    (debug: expected    ', fileDataExpected_3);
  152
+    assert.equal(fileData, fileDataExpected_3);
  153
+
  154
+    run_test_4();
  155
+  });
  156
+
  157
+  file.on('error', function(err) {
  158
+    cb_occurred += 'error ';
  159
+    console.log('    (debug: err event ', err);
  160
+    throw err;
  161
+  });
  162
+
  163
+  file.write(data, 'utf8');
  164
+  cb_occurred += 'write ';
  165
+
  166
+  file.end();
  167
+}
  168
+
  169
+
  170
+function run_test_4() {
  171
+  var file, options;
  172
+
  173
+  options = { start: -5,
  174
+              flags: 'r+' };
  175
+
  176
+  //  Error: start must be >= zero
  177
+  assert.throws(
  178
+    function() {
  179
+      file = fs.createWriteStream(filepath, options);
  180
+    },
  181
+    /start must be/
  182
+  );
  183
+
  184
+}
  185
+
  186
+run_test_1();
  187
+

0 notes on commit ca35782

Please sign in to comment.
Something went wrong with that request. Please try again.