fs: add fs.copyFile{Sync}
Fixes: https://github.com/nodejs/node/issues/14906 PR-URL: https://github.com/nodejs/node/pull/15034 Reviewed-By: Timothy Gu <timothygu99@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
6281361504
commit
11b7428832
@ -750,6 +750,88 @@ Returns an object containing commonly used constants for file system
|
||||
operations. The specific constants currently defined are described in
|
||||
[FS Constants][].
|
||||
|
||||
## fs.copyFile(src, dest[, flags], callback)
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `src` {string|Buffer|URL} source filename to copy
|
||||
* `dest` {string|Buffer|URL} destination filename of the copy operation
|
||||
* `flags` {number} modifiers for copy operation. **Default:** `0`
|
||||
* `callback` {Function}
|
||||
|
||||
Asynchronously copies `src` to `dest`. By default, `dest` is overwritten if it
|
||||
already exists. No arguments other than a possible exception are given to the
|
||||
callback function. Node.js makes no guarantees about the atomicity of the copy
|
||||
operation. If an error occurs after the destination file has been opened for
|
||||
writing, Node.js will attempt to remove the destination.
|
||||
|
||||
`flags` is an optional integer that specifies the behavior
|
||||
of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`,
|
||||
which causes the copy operation to fail if `dest` already exists.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
const fs = require('fs');
|
||||
|
||||
// destination.txt will be created or overwritten by default.
|
||||
fs.copyFile('source.txt', 'destination.txt', (err) => {
|
||||
if (err) throw err;
|
||||
console.log('source.txt was copied to destination.txt');
|
||||
});
|
||||
```
|
||||
|
||||
If the third argument is a number, then it specifies `flags`, as shown in the
|
||||
following example.
|
||||
|
||||
```js
|
||||
const fs = require('fs');
|
||||
const { COPYFILE_EXCL } = fs.constants;
|
||||
|
||||
// By using COPYFILE_EXCL, the operation will fail if destination.txt exists.
|
||||
fs.copyFile('source.txt', 'destination.txt', COPYFILE_EXCL, callback);
|
||||
```
|
||||
|
||||
## fs.copyFileSync(src, dest[, flags])
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* `src` {string|Buffer|URL} source filename to copy
|
||||
* `dest` {string|Buffer|URL} destination filename of the copy operation
|
||||
* `flags` {number} modifiers for copy operation. **Default:** `0`
|
||||
|
||||
Synchronously copies `src` to `dest`. By default, `dest` is overwritten if it
|
||||
already exists. Returns `undefined`. Node.js makes no guarantees about the
|
||||
atomicity of the copy operation. If an error occurs after the destination file
|
||||
has been opened for writing, Node.js will attempt to remove the destination.
|
||||
|
||||
`flags` is an optional integer that specifies the behavior
|
||||
of the copy operation. The only supported flag is `fs.constants.COPYFILE_EXCL`,
|
||||
which causes the copy operation to fail if `dest` already exists.
|
||||
|
||||
Example:
|
||||
|
||||
```js
|
||||
const fs = require('fs');
|
||||
|
||||
// destination.txt will be created or overwritten by default.
|
||||
fs.copyFileSync('source.txt', 'destination.txt');
|
||||
console.log('source.txt was copied to destination.txt');
|
||||
```
|
||||
|
||||
If the third argument is a number, then it specifies `flags`, as shown in the
|
||||
following example.
|
||||
|
||||
```js
|
||||
const fs = require('fs');
|
||||
const { COPYFILE_EXCL } = fs.constants;
|
||||
|
||||
// By using COPYFILE_EXCL, the operation will fail if destination.txt exists.
|
||||
fs.copyFileSync('source.txt', 'destination.txt', COPYFILE_EXCL);
|
||||
```
|
||||
|
||||
## fs.createReadStream(path[, options])
|
||||
<!-- YAML
|
||||
added: v0.1.31
|
||||
|
55
lib/fs.js
55
lib/fs.js
@ -1884,6 +1884,61 @@ fs.mkdtempSync = function(prefix, options) {
|
||||
};
|
||||
|
||||
|
||||
// Define copyFile() flags.
|
||||
Object.defineProperties(fs.constants, {
|
||||
COPYFILE_EXCL: { enumerable: true, value: constants.UV_FS_COPYFILE_EXCL }
|
||||
});
|
||||
|
||||
|
||||
fs.copyFile = function(src, dest, flags, callback) {
|
||||
if (typeof flags === 'function') {
|
||||
callback = flags;
|
||||
flags = 0;
|
||||
} else if (typeof callback !== 'function') {
|
||||
throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'callback', 'function');
|
||||
}
|
||||
|
||||
src = getPathFromURL(src);
|
||||
|
||||
if (handleError(src, callback))
|
||||
return;
|
||||
|
||||
if (!nullCheck(src, callback))
|
||||
return;
|
||||
|
||||
dest = getPathFromURL(dest);
|
||||
|
||||
if (handleError(dest, callback))
|
||||
return;
|
||||
|
||||
if (!nullCheck(dest, callback))
|
||||
return;
|
||||
|
||||
src = pathModule._makeLong(src);
|
||||
dest = pathModule._makeLong(dest);
|
||||
flags = flags | 0;
|
||||
const req = new FSReqWrap();
|
||||
req.oncomplete = makeCallback(callback);
|
||||
binding.copyFile(src, dest, flags, req);
|
||||
};
|
||||
|
||||
|
||||
fs.copyFileSync = function(src, dest, flags) {
|
||||
src = getPathFromURL(src);
|
||||
handleError(src);
|
||||
nullCheck(src);
|
||||
|
||||
dest = getPathFromURL(dest);
|
||||
handleError(dest);
|
||||
nullCheck(dest);
|
||||
|
||||
src = pathModule._makeLong(src);
|
||||
dest = pathModule._makeLong(dest);
|
||||
flags = flags | 0;
|
||||
binding.copyFile(src, dest, flags);
|
||||
};
|
||||
|
||||
|
||||
var pool;
|
||||
|
||||
function allocNewPool(poolSize) {
|
||||
|
@ -1164,10 +1164,6 @@ void DefineSystemConstants(Local<Object> target) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void DefineUVConstants(Local<Object> target) {
|
||||
NODE_DEFINE_CONSTANT(target, UV_UDP_REUSEADDR);
|
||||
}
|
||||
|
||||
void DefineCryptoConstants(Local<Object> target) {
|
||||
#if HAVE_OPENSSL
|
||||
NODE_DEFINE_STRING_CONSTANT(target,
|
||||
@ -1274,12 +1270,15 @@ void DefineConstants(v8::Isolate* isolate, Local<Object> target) {
|
||||
DefineErrnoConstants(err_constants);
|
||||
DefineWindowsErrorConstants(err_constants);
|
||||
DefineSignalConstants(sig_constants);
|
||||
DefineUVConstants(os_constants);
|
||||
DefineSystemConstants(fs_constants);
|
||||
DefineOpenSSLConstants(crypto_constants);
|
||||
DefineCryptoConstants(crypto_constants);
|
||||
DefineZlibConstants(zlib_constants);
|
||||
|
||||
// Define libuv constants.
|
||||
NODE_DEFINE_CONSTANT(os_constants, UV_UDP_REUSEADDR);
|
||||
NODE_DEFINE_CONSTANT(fs_constants, UV_FS_COPYFILE_EXCL);
|
||||
|
||||
os_constants->Set(OneByteString(isolate, "errno"), err_constants);
|
||||
os_constants->Set(OneByteString(isolate, "signals"), sig_constants);
|
||||
target->Set(OneByteString(isolate, "os"), os_constants);
|
||||
|
@ -212,6 +212,7 @@ void After(uv_fs_t *req) {
|
||||
case UV_FS_FCHMOD:
|
||||
case UV_FS_CHOWN:
|
||||
case UV_FS_FCHOWN:
|
||||
case UV_FS_COPYFILE:
|
||||
// These, however, don't.
|
||||
argc = 1;
|
||||
break;
|
||||
@ -961,6 +962,30 @@ static void Open(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
|
||||
static void CopyFile(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
if (!args[0]->IsString())
|
||||
return TYPE_ERROR("src must be a string");
|
||||
if (!args[1]->IsString())
|
||||
return TYPE_ERROR("dest must be a string");
|
||||
if (!args[2]->IsInt32())
|
||||
return TYPE_ERROR("flags must be an int");
|
||||
|
||||
BufferValue src(env->isolate(), args[0]);
|
||||
ASSERT_PATH(src)
|
||||
BufferValue dest(env->isolate(), args[1]);
|
||||
ASSERT_PATH(dest)
|
||||
int flags = args[2]->Int32Value();
|
||||
|
||||
if (args[3]->IsObject()) {
|
||||
ASYNC_DEST_CALL(copyfile, args[3], *dest, UTF8, *src, *dest, flags)
|
||||
} else {
|
||||
SYNC_DEST_CALL(copyfile, *src, *dest, *src, *dest, flags)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Wrapper for write(2).
|
||||
//
|
||||
// bytesWritten = write(fd, buffer, offset, length, position, callback)
|
||||
@ -1425,6 +1450,7 @@ void InitFs(Local<Object> target,
|
||||
env->SetMethod(target, "writeBuffers", WriteBuffers);
|
||||
env->SetMethod(target, "writeString", WriteString);
|
||||
env->SetMethod(target, "realpath", RealPath);
|
||||
env->SetMethod(target, "copyFile", CopyFile);
|
||||
|
||||
env->SetMethod(target, "chmod", Chmod);
|
||||
env->SetMethod(target, "fchmod", FChmod);
|
||||
|
104
test/parallel/test-fs-copyfile.js
Normal file
104
test/parallel/test-fs-copyfile.js
Normal file
@ -0,0 +1,104 @@
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const src = path.join(common.fixturesDir, 'a.js');
|
||||
const dest = path.join(common.tmpDir, 'copyfile.out');
|
||||
const { COPYFILE_EXCL, UV_FS_COPYFILE_EXCL } = fs.constants;
|
||||
|
||||
function verify(src, dest) {
|
||||
const srcData = fs.readFileSync(src, 'utf8');
|
||||
const srcStat = fs.statSync(src);
|
||||
const destData = fs.readFileSync(dest, 'utf8');
|
||||
const destStat = fs.statSync(dest);
|
||||
|
||||
assert.strictEqual(srcData, destData);
|
||||
assert.strictEqual(srcStat.mode, destStat.mode);
|
||||
assert.strictEqual(srcStat.size, destStat.size);
|
||||
}
|
||||
|
||||
common.refreshTmpDir();
|
||||
|
||||
// Verify that flags are defined.
|
||||
assert.strictEqual(typeof COPYFILE_EXCL, 'number');
|
||||
assert.strictEqual(typeof UV_FS_COPYFILE_EXCL, 'number');
|
||||
assert.strictEqual(COPYFILE_EXCL, UV_FS_COPYFILE_EXCL);
|
||||
|
||||
// Verify that files are overwritten when no flags are provided.
|
||||
fs.writeFileSync(dest, '', 'utf8');
|
||||
const result = fs.copyFileSync(src, dest);
|
||||
assert.strictEqual(result, undefined);
|
||||
verify(src, dest);
|
||||
|
||||
// Verify that files are overwritten with default flags.
|
||||
fs.copyFileSync(src, dest, 0);
|
||||
verify(src, dest);
|
||||
|
||||
// Throws if destination exists and the COPYFILE_EXCL flag is provided.
|
||||
assert.throws(() => {
|
||||
fs.copyFileSync(src, dest, COPYFILE_EXCL);
|
||||
}, /^Error: EEXIST|ENOENT:.+, copyfile/);
|
||||
|
||||
// Throws if the source does not exist.
|
||||
assert.throws(() => {
|
||||
fs.copyFileSync(src + '__does_not_exist', dest, COPYFILE_EXCL);
|
||||
}, /^Error: ENOENT: no such file or directory, copyfile/);
|
||||
|
||||
// Copies asynchronously.
|
||||
fs.unlinkSync(dest);
|
||||
fs.copyFile(src, dest, common.mustCall((err) => {
|
||||
assert.ifError(err);
|
||||
verify(src, dest);
|
||||
|
||||
// Copy asynchronously with flags.
|
||||
fs.copyFile(src, dest, COPYFILE_EXCL, common.mustCall((err) => {
|
||||
assert(
|
||||
/^Error: EEXIST: file already exists, copyfile/.test(err.toString())
|
||||
);
|
||||
}));
|
||||
}));
|
||||
|
||||
// Throws if callback is not a function.
|
||||
common.expectsError(() => {
|
||||
fs.copyFile(src, dest, 0, 0);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: TypeError,
|
||||
message: 'The "callback" argument must be of type function'
|
||||
});
|
||||
|
||||
// Throws if the source path is not a string.
|
||||
assert.throws(() => {
|
||||
fs.copyFileSync(null, dest);
|
||||
}, /^TypeError: src must be a string$/);
|
||||
|
||||
// Throws if the source path is an invalid path.
|
||||
common.expectsError(() => {
|
||||
fs.copyFileSync('\u0000', dest);
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: Error,
|
||||
message: 'The "path" argument must be of type string without null bytes.' +
|
||||
' Received type string'
|
||||
});
|
||||
|
||||
// Throws if the destination path is not a string.
|
||||
assert.throws(() => {
|
||||
fs.copyFileSync(src, null);
|
||||
}, /^TypeError: dest must be a string$/);
|
||||
|
||||
// Throws if the destination path is an invalid path.
|
||||
common.expectsError(() => {
|
||||
fs.copyFileSync(src, '\u0000');
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
type: Error,
|
||||
message: 'The "path" argument must be of type string without null bytes.' +
|
||||
' Received type string'
|
||||
});
|
||||
|
||||
// Errors if invalid flags are provided.
|
||||
assert.throws(() => {
|
||||
fs.copyFileSync(src, dest, -1);
|
||||
}, /^Error: EINVAL: invalid argument, copyfile/);
|
Loading…
x
Reference in New Issue
Block a user