1. copyFile

The copyFile() method has the simplest operation and can copy the file directly to the target directory.

1
fs.copyFile('./a.txt', './dist/b.txt');

Asynchronously copies src to dest. By default, if dest already exists, it is overwritten. No arguments are given to the callback function, except for a possible exception. Node.js does not guarantee the atomicity of the copy operation. If an error occurs after opening the target file for writing, Node.js will try to delete the target file.

However, this method has one disadvantage: the target directory must exist (it does not create directories automatically), and if it does not exist, an exception will be thrown. So when you use copyFile() method, you must make sure that the directory must exist, if not, you need to use fs.mkdir() or fs.mkdirSync() to create the directory.

Also, copyFile() cannot copy directories.

2. readFile and writeFile

Reads the contents of the src file and then writes it to the target file.

This way is suitable for, if you need to modify the content during the copying process, then write to the target file.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fs.readFile('./a.txt', { encoding: 'utf8' }, (err, data) => {
  if (err) {
    console.error(err);
    return;
  }
  data = data.replace(/hello/gi, 'world');
  fs.writeFile('./b.txt', data, (err) => {
    if (err) {
      console.error(err);
    }
  });
});

The disadvantage is the same as copyFile() above, writeFile() can only write to files in already existing directories, readFile() is used to read the contents of the file, so it can’t copy directories either.

The advantage is that the contents can be modified during the copying process.

3. createReadStream and createWriteStream

readFile and writeFile manipulate data in whole blocks, which can strain system resources if the file is large. The createReadStream and createWriteStream manipulate data in a stream fashion.

1
fs.createReadStream('./a.txt').pipe(fs.createWriteStream(`./b.txt`));

4. cp

Starting with version 16.7.0, nodejs has a new fs.cp() method that copies the entire directory structure from src to dest asynchronously, including subdirectories and files.

This method can copy both a particular file and a directory. The recursive property in the configuration needs to be set to true when a directory needs to be copied.

To copy files.

1
2
3
4
5
6
// 复制文件
fs.cp('./a.txt', './aa/b.txt', (err) => {
  if (err) {
    console.error(err);
  }
});

Copy the entire directory, including subdirectories.

1
2
3
4
5
6
// 复制目录
fs.cp('./aa', './bb', { recursive: true }, (err) => {
  if (err) {
    console.error(err);
  }
});

As you can see, this method works much better than the previous one:

  1. there is no more need to make sure that the dest directory must exist, if the dest directory does not exist, it will be created automatically (no matter how many levels of directories).
  2. you can copy the entire folder, including the subdirectories, without having to do it recursively and separately.

The only thing you need to do is to make sure you have the right version of nodejs!

What if you have a low version of nodejs and you want to copy all the files in the folder? In addition to the linux native cp command in the next section, we can also use the recursive way to copy all the files.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
 * 复制文件夹到目标文件夹
 * @param {string} src 源目录
 * @param {string} dest 目标目录
 * @param {function} callback 回调
 */
const copyDir = (src, dest, callback) => {
  const copy = (copySrc, copyDest) => {
    fs.readdir(copySrc, (err, list) => {
      if (err) {
        callback(err);
        return;
      }
      list.forEach((item) => {
        const ss = path.resolve(copySrc, item);
        fs.stat(ss, (err, stat) => {
          if (err) {
            callback(err);
          } else {
            const curSrc = path.resolve(copySrc, item);
            const curDest = path.resolve(copyDest, item);

            if (stat.isFile()) {
              // 文件,直接复制
              fs.createReadStream(curSrc).pipe(fs.createWriteStream(curDest));
            } else if (stat.isDirectory()) {
              // 目录,进行递归
              fs.mkdirSync(curDest, { recursive: true });
              copy(curSrc, curDest);
            }
          }
        });
      });
    });
  };

  fs.access(dest, (err) => {
    if (err) {
      // 若目标目录不存在,则创建
      fs.mkdirSync(dest, { recursive: true });
    }
    copy(src, dest);
  });
};

Usage.

1
copyDir('./aa', './abc/ddd');

5. cp commands in linux

We can use exec or spawn in child_process to execute the native commands in linux. The cp command in linux is used to copy files or directories.

1
2
3
4
5
6
const { exec, spawn } = require('child_process');

exec('cp ./aa/a.txt ./bb/b.txt'); // 复制文件时,需要确保目标目录存在
exec('cp -r ./aa ./bb/cc/dd'); // 复制文件夹,目标目录可以自动创建

spawn('cp', ['-r', './aa', './bb/cc/dd']);