Commit 00ba4d3a authored by Julien Fontanet's avatar Julien Fontanet

feat(create{Read,Write}Stream): support opened files

parent 3bffb302
...@@ -235,6 +235,14 @@ smb2Client.createReadStream('path\\to\\the\\file', function(err, readStream) { ...@@ -235,6 +235,14 @@ smb2Client.createReadStream('path\\to\\the\\file', function(err, readStream) {
}); });
``` ```
Supported options:
- `autoClose`: whether the `fd` should be closed at the end or on error, default `true`
- `end`: offset in the file after which to stop reading, default `Infinity`
- `fd`: if specified, the path will be ignored and this opened file will be used instead
- `flags`: see [Node documentation](https://nodejs.org/dist/latest-v10.x/docs/api/fs.html#fs_file_system_flags), default `'r'`
- `start`: offset in the file from which to start reading, default `0`
> `smb2Client.createWriteStream ( fileName, [options], callback )` > `smb2Client.createWriteStream ( fileName, [options], callback )`
Returns a write stream on the file. Returns a write stream on the file.
...@@ -251,6 +259,13 @@ smb2Client.createWriteStream('path\\to\\the\\file', function(err, readStream) { ...@@ -251,6 +259,13 @@ smb2Client.createWriteStream('path\\to\\the\\file', function(err, readStream) {
}); });
``` ```
Supported options:
- `autoClose`: whether the `fd` should be closed at the end or on error, default `true`
- `fd`: if specified, the path will be ignored and this opened file will be used instead
- `flags`: see [Node documentation](https://nodejs.org/dist/latest-v10.x/docs/api/fs.html#fs_file_system_flags), default `'wx'`
- `start`: offset in the file from which to start writing, default `0`
### Low-level API ### Low-level API
```javascript ```javascript
......
...@@ -14,7 +14,11 @@ module.exports = function createReadStream(path, options, cb) { ...@@ -14,7 +14,11 @@ module.exports = function createReadStream(path, options, cb) {
var connection = this; var connection = this;
request('open', { path: path }, connection, function(err, file) { var fd = options.fd;
var isFd = fd != null;
var shouldClose = !isFd || (options.autoClose == null || options.autoClose);
function onOpen(err, file) {
if (err != null) { if (err != null) {
return cb(err); return cb(err);
} }
...@@ -29,14 +33,16 @@ module.exports = function createReadStream(path, options, cb) { ...@@ -29,14 +33,16 @@ module.exports = function createReadStream(path, options, cb) {
var close = request.bind(undefined, 'close', file, connection); var close = request.bind(undefined, 'close', file, connection);
var stream = new Readable(); var stream = new Readable();
stream._destroy = function(err, cb) { if (shouldClose) {
close(function(err2) { stream._destroy = function(err, cb) {
if (err != null) { close(function(err2) {
return cb(err2); if (err2 != null) {
} return cb(err2);
cb(err); }
}); cb(err);
}; });
};
}
var running = false; var running = false;
stream._read = function(size) { stream._read = function(size) {
if (running) { if (running) {
...@@ -44,9 +50,11 @@ module.exports = function createReadStream(path, options, cb) { ...@@ -44,9 +50,11 @@ module.exports = function createReadStream(path, options, cb) {
} }
if (offset >= end) { if (offset >= end) {
return close(function() { return shouldClose
stream.push(null); ? close(function() {
}); stream.push(null);
})
: stream.push(null);
} }
running = true; running = true;
...@@ -70,5 +78,11 @@ module.exports = function createReadStream(path, options, cb) { ...@@ -70,5 +78,11 @@ module.exports = function createReadStream(path, options, cb) {
); );
}; };
cb(null, stream); cb(null, stream);
}); }
if (isFd) {
onOpen(null, fd);
} else {
request('open', { path: path }, connection, onOpen);
}
}; };
...@@ -12,64 +12,78 @@ module.exports = function createWriteStream(path, options, cb) { ...@@ -12,64 +12,78 @@ module.exports = function createWriteStream(path, options, cb) {
} }
var connection = this; var connection = this;
request(
'create',
{
createDisposition: parseFlags((options != null && options.flags) || 'wx'),
path: path,
},
connection,
function(err, file) {
if (err != null) {
return cb(err);
}
var offset = new BigInt(8, (options != null && options.start) || 0); var fd = options.fd;
var isFd = fd != null;
var shouldClose = !isFd || (options.autoClose == null || options.autoClose);
var close = request.bind(undefined, 'close', file, connection); function onCreate(err, file) {
if (err != null) {
return cb(err);
}
var offset = new BigInt(8, (options != null && options.start) || 0);
function write(buffer, i, cb) { function write(buffer, i, cb) {
var j = i + constants.MAX_WRITE_LENGTH; var j = i + constants.MAX_WRITE_LENGTH;
var chunk = buffer.slice(i, j); var chunk = buffer.slice(i, j);
request( request(
'write', 'write',
{ {
Buffer: chunk, Buffer: chunk,
FileId: file.FileId, FileId: file.FileId,
Offset: offset.toBuffer(), Offset: offset.toBuffer(),
}, },
connection, connection,
function(err) { function(err) {
if (err != null) { if (err != null) {
return cb(err); return cb(err);
} }
offset = offset.add(chunk.length); offset = offset.add(chunk.length);
if (j < buffer.length) { if (j < buffer.length) {
return write(buffer, j, cb); return write(buffer, j, cb);
}
cb();
} }
); cb();
} }
);
}
var stream = new Writable(); var stream = new Writable();
if (shouldClose) {
var close = request.bind(undefined, 'close', file, connection);
stream._destroy = function(err, cb) { stream._destroy = function(err, cb) {
close(function(err2) { close(function(err2) {
if (err != null) { if (err2 != null) {
return cb(err2); return cb(err2);
} }
cb(err); cb(err);
}); });
}; };
stream._final = close; stream._final = close;
stream._write = function(chunk, encoding, next) {
write(
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding),
0,
next
);
};
cb(null, stream);
} }
); stream._write = function(chunk, encoding, next) {
write(
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding),
0,
next
);
};
cb(null, stream);
}
if (isFd) {
onCreate(null, fd);
} else {
request(
'create',
{
createDisposition: parseFlags(
(options != null && options.flags) || 'wx'
),
path: path,
},
connection,
onCreate
);
}
}; };
...@@ -1714,6 +1714,12 @@ ...@@ -1714,6 +1714,12 @@
"pinkie-promise": "^2.0.0" "pinkie-promise": "^2.0.0"
} }
}, },
"golike-defer": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/golike-defer/-/golike-defer-0.4.1.tgz",
"integrity": "sha512-x8cq/Fvu32T8cnco3CBDRF+/M2LFmfSIysKfecX09uIK3cFdHcEKBTPlPnEO6lwrdxfjkOIU6dIw3EIlEJeS1A==",
"dev": true
},
"graceful-fs": { "graceful-fs": {
"version": "4.1.15", "version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
...@@ -1995,8 +2001,7 @@ ...@@ -1995,8 +2001,7 @@
"inherits": { "inherits": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
"dev": true
}, },
"inquirer": { "inquirer": {
"version": "6.2.1", "version": "6.2.1",
...@@ -4458,19 +4463,13 @@ ...@@ -4458,19 +4463,13 @@
} }
}, },
"readable-stream": { "readable-stream": {
"version": "2.3.6", "version": "3.0.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==",
"dev": true,
"optional": true,
"requires": { "requires": {
"core-util-is": "~1.0.0", "inherits": "^2.0.3",
"inherits": "~2.0.3", "string_decoder": "^1.1.1",
"isarray": "~1.0.0", "util-deprecate": "^1.0.1"
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
} }
}, },
"regex-not": { "regex-not": {
...@@ -4612,8 +4611,7 @@ ...@@ -4612,8 +4611,7 @@
"safe-buffer": { "safe-buffer": {
"version": "5.1.2", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"dev": true
}, },
"safe-regex": { "safe-regex": {
"version": "1.1.0", "version": "1.1.0",
...@@ -4990,11 +4988,9 @@ ...@@ -4990,11 +4988,9 @@
} }
}, },
"string_decoder": { "string_decoder": {
"version": "1.1.1", "version": "1.2.0",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
"dev": true,
"optional": true,
"requires": { "requires": {
"safe-buffer": "~5.1.0" "safe-buffer": "~5.1.0"
} }
...@@ -5154,6 +5150,32 @@ ...@@ -5154,6 +5150,32 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true "dev": true
}, },
"readable-stream": {
"version": "2.3.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"optional": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "~5.1.0"
}
},
"tap-parser": { "tap-parser": {
"version": "5.4.0", "version": "5.4.0",
"resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-5.4.0.tgz", "resolved": "https://registry.npmjs.org/tap-parser/-/tap-parser-5.4.0.tgz",
...@@ -5436,9 +5458,7 @@ ...@@ -5436,9 +5458,7 @@
"util-deprecate": { "util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
"dev": true,
"optional": true
}, },
"uuid": { "uuid": {
"version": "3.3.2", "version": "3.3.2",
......
...@@ -20,7 +20,8 @@ ...@@ -20,7 +20,8 @@
"url": "https://github.com/marsaud/node-smb2" "url": "https://github.com/marsaud/node-smb2"
}, },
"dependencies": { "dependencies": {
"ntlm": "~0.1.1" "ntlm": "~0.1.1",
"readable-stream": "^3.0.6"
}, },
"keywords": [ "keywords": [
"SMB", "SMB",
...@@ -42,6 +43,8 @@ ...@@ -42,6 +43,8 @@
"eslint-plugin-node": "^8.0.0", "eslint-plugin-node": "^8.0.0",
"eslint-plugin-promise": "^4.0.1", "eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0", "eslint-plugin-standard": "^4.0.0",
"get-stream": "^4.1.0",
"golike-defer": "^0.4.1",
"husky": "^1.2.0", "husky": "^1.2.0",
"lint-staged": "^8.1.0", "lint-staged": "^8.1.0",
"prettier": "^1.15.3", "prettier": "^1.15.3",
......
const asyncFn = require('promise-toolbox/asyncFn'); const asyncFn = require('promise-toolbox/asyncFn');
const defer = require('golike-defer').default;
const finished = require('readable-stream').finished;
const fromCallback = require('promise-toolbox/fromCallback');
const fs = require('fs'); const fs = require('fs');
const getStream = require('get-stream');
const path = require('path'); const path = require('path');
const t = require('tap'); const t = require('tap');
const TOML = require('@iarna/toml'); const TOML = require('@iarna/toml');
...@@ -45,6 +49,39 @@ const tests = { ...@@ -45,6 +49,39 @@ const tests = {
yield client.unlink(file); yield client.unlink(file);
} }
}), }),
createReadStream: defer(
asyncFn(function*($d, client) {
yield client.writeFile(file, data);
$d.call(client, 'unlink', file);
const fd = yield client.open(file, 'r');
$d.call(client, 'close', fd);
t.same(
yield getStream.buffer(
yield client.createReadStream('', { autoClose: false, fd })
),
data
);
})
),
createWriteStream: defer(
asyncFn(function*($d, client) {
const fd = yield client.open(file, 'w');
$d.call(client, 'unlink', file);
$d.call(client, 'close', fd);
const stream = yield client.createWriteStream('', {
autoClose: false,
fd,
});
yield fromCallback(cb => {
finished(stream, cb);
stream.end(data);
});
t.same(yield client.readFile(file), data);
})
),
rmdir: function(client) { rmdir: function(client) {
return client.rmdir(dir); return client.rmdir(dir);
}, },
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment