在Node.js中操作文件系统(一)

在Node.js中操作文件系统

     在Node.js中,使用fs模块来实现所有有关文件及目录的创建,写入及删除操作。在fs模块中,所有对文件及目录的操作都可以使用同步与异步这两种方法。比如在执行读文件操作时,可以使用readFile与readFileSync方法。在所有这些方法中,方法名中具有Sync后缀的方法均为同步方法,而不具有Sync后缀的方法均为异步方法。两者的区别是:在使用同步方法执行操作结束之前,不能执行后续代码。比如如下:

var  fs = require(“fs”);
var  data = fs.readFileSync(‘./index.html’,’utf8’);
// 等待操作结果返回,然后利用该结果。
console.log(data);

而异步方法将操作结果作为回调函数的参数进行返回。在方法调用之后,可以立即执行后续代码,代码如下所示:

var fs = require(‘fs’);
fs.readFile(‘./index.html’,’utf8’,function(err,data){
    console.log(data);
})
Console.log(1);// 这句代码先执行

但是异步的方法是不能保证顺序执行的。比如如下

fs.readFile(‘./index.html’,’utf8’,function(err,data){
    console.log(data);
})

fs.readFile(‘./index2.html’,’utf8’,function(err,data){
    console.log(data);
})

上面2个异步方法,我们不能保证那个异步方法先执行完,如果我们先要第一个方法执行完后,在执行第二个方法,那么代码需要如下写了:

fs.readFile(‘./index.html’,’utf8’,function(err,data){
    console.log(data);
    fs.readFile(‘./index2.html’,’utf8’,function(err,data){
         console.log(data);
    })

})

我们先来看看文件的读写操作;如下:

一:文件的读操作;

读取文件的方法可以使用fs模块中的readFile与readFileSync方法,我们先来了解readFile方法;如下:

  fs.readFile(filename,[options],callback);

在readFile方法中,有三个参数,其中filename参数与callback参数是必须的,options参数是可选的,filename参数用于指定读取文件的完整文件路径及文件名。Options参数值为一个对象,在其中指定读取文件时需要使用的选项,在该参数值对象中可以使用flag属性指定对该文件采取什么操作,默认值为 ’r’(如果指定读取的文件不存在,则抛出异常)。可以指定的值如下:

'r’: 读取文件,如果文件不存在则抛出异常。

‘r+’: 读取并写入文件,如果文件不存在则抛出异常。

‘w’:  写入文件。如果文件不存在则创建该文件,如果该文件已存在则清空文件内容。

 ‘wx’: 作用与’w’类似。

‘w+’ 读取并写入文件。如果文件不存在则创建该文件,如果该文件已存在则清空文件内容。

‘wx+’ 作用与’w+’类似。

‘a’ 追加写入文件,如果该文件不存在则创建该文件。

‘ax’ 作用与’a’类似。

‘a+’ 读取并追加写入文件,如果文件不存在则创建该文件。

 ‘ax+’ 作用与’a+’类似。

在options参数值中,可使用encoding属性指定使用何种编码格式来读取该文件,可指定属性值为’utf8’,’ascii’与’base64’. Callback参数用于文件读取完毕执行的回调函数。比如下面的方法:

Function(err,data){}

其中第一个参数值为读取文件操作失败时候触发的错误对象,第二个参数值为读取到的文件内容。  当在options参数值对象中使用encoding属性指定编码格式时,该参数值为将文件内容指定编码格式经过编码后的字符串,当不指定编码格式时,该参数值为一个存放了文件中原始二进制内容的缓存区对象。

我们可以先来看一个简单的readFile方法的使用demo,比如在我的根目录下放一个test.txt文件,内容如下:

这是第一行

这是第二行

然后在应用程序的根目录下放一个app.js文件,代码如下:

var fs = require('fs');

fs.readFile('./test.txt',function(err,data){
    if(err) {
        console.log("读取文件时发生错误");
    }else {
        console.log(data);
    }
});

我们读取test.txt文件中的内容,如果读取成功,则在控制台将其输出,如果读取失败,则在控制台中输出 ”读取文件时发生错误”.

注意:test.txt文件需要保存编码为utf-8

如下所示:

由上可知:控制台输出的是存取了文件原始二进制数据的缓存区中的内容,如果要将该内容以字符串的形式输出,有2种方式如下:

代码改为如下1:

var fs = require('fs');
fs.readFile('./test.txt',function(err,data){
    if(err) {
        console.log("读取文件时发生错误");
    }else {
        console.log(data.toString());
    }
});

第二种形式是:我们可以指定options参数并将encoding属性值指定为某种编码格式,则回调函数中的第二个参数值返回将文件内容根据指定编码格式进行编码后的字符串。如下代码:

var fs = require('fs');
fs.readFile('./test.txt','utf8',function(err,data){
    if(err) {
        console.log("读取文件时发生错误");
    }else {
        console.log(data);
    }
});

运行如下:

在使用同步方式读取文件时,使用readFileSync方法,该方法使用如下:

var data = fs.readFileSync(filename,[options]);

在readFileSync方法中,使用两个参数,这两个参数的含义及指定方法与readFile方法中使用filename参数与options 参数的含义及指定方法完全相同。

如下代码:

var fs = require('fs');
try{
    var data = fs.readFileSync('./test.txt','utf8');
    console.log(data);
}catch(e){
    console.log("读取文件时发生错误。");
}

执行效果如上所示:

二:文件的写操作;

在完整写入一个文件时,我们可以使用fs模块的writeFile方法或writeFileSync方法。使用方法如下所示:

fs.writeFile(filename,data,[options],callback);

     在writeFile方法中,使用4个参数,其中filename,data,callback为必须指定的参数,[options]为可选参数,filename参数用于指定需要被写入文件的完整文件路径及文件名,data参数用于指定需要写入的内容,参数值可以为一个字符串或一个buffer对象,该字符串或缓存区中的内容将被完整地写入到文件中。Options参数值为一个对象,在其中指定写入文件时需要使用的选项,在该参数值对象中可以使用如下所示的属性值。

1. flag属性:用于指定对该文件采取何种操作,默认值’w’(文件不存在时创建该文件,文件已存在时重写该文件,)可指定值及其含义与readFile方法中使用的options参数值中的flag属性的可指定值及其含义相同。

2. mode属性:用于指定当文件被打开时对该文件的读写权限,默认值为0666(可读写)。该属性值及fs模块中的各方法中的mode参数值的指定方法如下所示:使用4个参数组成mode属性值或mode参数值,其中第一个数字必须为0,第二个数字用于规定文件或目录所有者的权限,第三个数字用于规定文件或目录所有者所属用户组的权限,第四个数字规定其他人的权限,可以设定的数字如下所示:

1. 执行权限

2. 写权限

3. 读权限

如果需要设置读写等复合权限,可以对以上三个数字进行加运算,比如使用 2+4=6来设置读写权限。

下面我们来看看demo,其中app.js代码如下:

var fs = require('fs');
fs.writeFile('./message.txt',"这是第一行。
这是第二行.",function(err){
    if(err) {
        console.log("写文件操作失败");
    }else {
        console.log("写文件操作成功");
    }
});

在上面的代码中,我们将在应用程序的根目录创建一个message.txt文件并在其中写入两行文字。我们可以去看下根目录就知道,就生成了一个message.txt文件了。

接下来我们看一个在文件中写入缓存区中数据的代码,如下代码:

var fs = require('fs');
var data = new Buffer("我喜爱编程");
fs.writeFile('./message.txt',data,function(err){
    if(err) {
        console.log("写文件操作失败");
    }else {
        console.log("写文件操作成功");
    }
});

如上代码,我们可以看到在message.txt中,写文件操作成功,我喜爱编程几个字也在message.txt中了。

接下来我们可以通过options参数中设置flag属性值的方式修改对文件的操作方式,比如在代码中,我们可以使用writeFile方法在message.txt文件尾部追加”这是追加的数据” 文字。比如如下代码:

var fs = require('fs');
var options = {
    flag: 'a'
};
fs.writeFile('./message.txt','这是追加的数据。',options,function(err){
    if(err) {
        console.log("写文件操作失败");
    }else {
        console.log("写文件操作成功");
    }
});

进入文件的根目录运行下 即可 可以看到,文字被添加进去了。

内容变成如下:

我喜爱编程这是追加的数据。

可以通过在options参数中设置encoding属性值的方式指定在文件中写入数据时使用的编码方式,比如如下我在我的根目录下有2张不同的图片,分别是1.gif图片和2.gif图片,现在我们可以先使用readFile读取1.gif图片后使用base64编码,在readFile方法的回调函数中使用writeFile方法且该方法中使用base64编码将读取到1.gif图片复制到2.gif图片上,也就是说对之前的2.gif图片进行覆盖。

代码如下:

var fs = require('fs');
fs.readFile("./1.gif",'base64',function(err,data){
    fs.writeFile('./2.gif',data.toString(),'base64',function(err){
        if(err) {
            console.log("写文件操作失败");
        }else {
            console.log("写文件操作成功");
        }
    });
});

运行下即可,然后我们继续查看2.gif图片可以看到,和1.gif图片一样的。

writeFileSync:

在使用同步方式写入文件时,使用writeFileSync方法,该方法使用如下:

   fs.writeFileSync(filename,data,[options]);

上面的同步方法和writeSync方法中放filename参数,data参数,[options]参数的含义及指定方法完全相同。

appendFile和appendFileSync方法

在将一个字符串或一个缓存区中的数据追加到一个文件底部时,我们可以使用fs模块中的appendFile和appendFileSync方法。

   fs.appendFile(filename,data,[options],callback);

在appendFile方法中,使用4个参数,其作用及指定方法与writeFile方法所使用的4个参数的作用与指定方法大致相同,区别在于,在options参数值对象中,flag属性的默认属性值为’a’(在文件底部追加写入数据,如果文件不存在,则创建该文件)。

如下代码:

var fs = require('fs');
fs.appendFile('./message.txt','这是追加的数据2。','utf8',function(err){
    if(err) {
        console.log("写文件操作失败");
    }else {
        console.log("写文件操作成功");
    }
});

运行下即可看到数据了。如果message.txt文件不存在,则创建该文件。

在使用同步方式在文件底部追加数据时,可以使用appendFileSync方法,该方法的使用方式如下:

  fs.appendFileSync(filename,data,[options]);

该方法的参数和appendFile方法的参数的含义和指定方法完全相同。

三:从指定位置处开始读写文件。

open:

如何从指定位置处开始读写文件的处理,我们首先需要使用fs模块中的open方法或openSync方法打开文件。open方法使用方式如下:

  fs.open(filename,flags,[mode],callback);

在open方法中,可以使用4个参数,其中filename,flags参数与callback参数是必须项。mode参数是可选项,filename,flags,[mode]参数的作用及其指定方法与writeFile相同。

Callback参数用于指定文件打开操作执行完毕时的回调函数,如下所示:

  Function(err,id){}

其中第一参数值为打开文件操作失败时所触发的错误对象,第二个参数值为一个整数值,代表打开文件时返回的文件描述符

如下demo

var fs = require('fs');
fs.open('./message.txt','r',function(err,id){
    console.log(id);
});

截图如下:

openSync:

在使用同步方式打开文件时,使用openSync方法,如下所示:

var  fd = fs.openSync(filename,flags,[mode]); 参数与上面open参数一样的。

在打开文件后,可以在回调函数中使用fs模块中的read方法或readSync方法从文件的指定位置处读取文件,也可以调用fs模块中的write方法或writeSync方法从文件的指定处开始写入数据。

read:

下面我们下来看看read方法,该方法从文件的指定位置处读取文件,一直读取到文件底部,然后将读取到的内容输出到一个缓存区中,该方法使用如下所示:

fs.read(fd,buffer,offset,length,position,callback);

在read方法中,使用6个参数,其中fd参数值必须为open方法所使用的回调函数中返回文件描述符或openSync方法返回的文件描述符; buffer参数值为一个Buffer对象,用于指定将文件数据读取到那个缓存区中,offset参数值,length参数值与position参数值均为一个整数,其中offset参数用于指定向缓存区中写入数据时的开始写入位置(以字节为单位),length参数用于指定从文件中读取的字节数,position参数用于指定读取文件时的开始位置(以字节为单位)。Callback参数用于指定文件读取操作执行完毕时执行的回调函数,如下

Function (err,bytesRead,buffer){}

Callback有3个参数,err为读取文件操作失败时所触发的错误对象;bytesRead为一个整数值,代表实际读取的字节数,buffer为被读取的缓存区的对象。

我们来看看代码如下,我们将使用read方法从应用程序根目录下的内容为”我喜爱编程”字符串的message.txt文件中将”喜爱编”这三个字符读取到一个缓存区中,然后从缓存区中读取这三个字符并将其在控制台输出;如下代码:

var fs = require('fs');
fs.open('./message.txt','r',function(err,fd){
    var buf = new Buffer(255);
    // 一个汉字的utf编码为三个字节数据
    fs.read(fd,buf,0,9,3,function(err,bytesRead,buffer){
        console.log(buffer.slice(0,bytesRead).toString());
    });
});

演示如下:

其中message.txt内容如下:

我喜爱编程这是追加的数据。这是追加的数据2。

上面代码是先打开message.txt文件,然后offset参数用于指定向缓存区中写入数据时的开始写入位置,为0;length=9 意思是:指定从文件中读取的字节数,由于一个汉字的utf编码为3个字节数据,因此中文为3个。Position=3 用于指定读取文件时的开始位置,因此从”喜”字开始读取。

read方法的position参数值用于指定读取文件时的开始位置(以字节为单位),如果该参数为null,将从文件的当前读取位置处(前一次读取时的开始位置+读取字节数)开始读取文件。如下代码:

var fs = require('fs');
fs.open('./message.txt','r',function(err,fd){
    var buf = new Buffer(255);
    // 一个汉字的utf编码为三个字节数据
    fs.read(fd,buf,0,9,3,function(err,bytesRead,buffer){
        console.log(buffer.slice(0,bytesRead).toString());
        // 从文件的当前读取位置继续往下读取
        fs.read(fd,buf,0,3,null,function(err,bytesRead,buffer){
            console.log(buffer.slice(0,bytesRead).toString());
        });
    });
});

演示如下:

readSync

在使用同步方式打开文件时,使用readSync方法,代码如下:

var  bytesRead = fs.readSync(fd,buffer,offset,length,position);

同步中的readSync和read方法里面的参数完全相同,该方法返回实际从文件中读取到的字节数。

下面是同步方式的代码如下:

var fs = require('fs');
fs.open('./message.txt','r',function(err,fd){
    var buf = new Buffer(255);
    // 一个汉字的utf编码为三个字节数据
    var bytesRead = fs.readSync(fd,buf,0,9,3);
    console.log(bytesRead);
    console.log(buf.slice(0,bytesRead).toString());
});

截图如下:

write方法和writeSync方法

在打开文件之后,可以使用fs模块中的write方法或writeSync方法从一个缓存区中读取数据并且从文件的指定处开始写入数据。

write方法使用如下:

fs.write(fd,buffer,offset,length,position,callback)

在write方法中,使用6个参数,fd参数值必须为open方法或者openSync方法所使用的回调函数中返回的文件描述符;buffer参数值为一个Buffer对象,用于指定从哪个缓存区中读取数据,offset,length及position参数值均为一个整数值,其中offset参数用于指定从缓存区中读取数据时的开始读取位置(以字节为单位);length参数用于指定从缓存区中读取的字节数,position参数值用于指定写入文件时的开始位置(以字节为单位)。

Callback参数用于指定文件写入操作执行完毕时执行的回调函数,如下:

Function (err,written,buffer){}

该回调使用三个参数,err参数为写入文件操作失败时所触发的错误对象;written参数值为一个整数值,代表被写入的字节数;buffer参数值为一个Buffer对象,代表被读取的缓存区对象;

下面我们来看看write方法的demo,我们将首先使用”我喜爱编程”字符串创建一个缓存区对象,然后使用write方法从缓存区中读取”喜爱编”这三个字符并将其写入message.txt文件中,代码如下:

var fs = require('fs');
var buf = new Buffer("我喜爱编程");
fs.open('./message.txt','w',function(err,fd){
    fs.write(fd,buf,3,9,0,function(err,written,buffer){
        if(err) {
            console.log("写文件操作失败");
        }else {
            console.log("写文件操作成功");
        }
    });
});

运行命令后,可以打开message.txt文件,可以看到内容为:‘喜爱编’

Write方法的position参数值用于指定写入文件时的开始位置(以字节为单位),当参数值为null时,将从文件的当前被写入位置处(前一次写入时的开始位置+写入字节数)开始写入文件。

我们接着修改app.js代码,在将 ”喜爱编”字符串写入到应用程序根目录的message.txt文件中后,追加写入”程”字符串。代码如下

var fs = require('fs');
var buf = new Buffer("我喜爱编程");
fs.open('./message.txt','a',function(err,fd){
    fs.write(fd,buf,3,9,0,function(err,written,buffer){
        fs.write(fd,buf,12,3,null,function(err,written,buffer){
            if(err) {
                console.log("写文件操作失败");
            }else {
                console.log("写文件操作成功");
            }
        });
    });
});

Message.txt内容如下:

喜爱编程

在使用同步方式写入文件时,使用writeSync方法,使用方式如下:

fs.writeSync(fd,buffer,offset,length,position);

上面的5个参数与write方法的参数完全相同。

close方法与closeSync方法

在fs模块中,提供close方法与closeSync方法以关闭文件;

使用如下:

fs.close(fd,[callback]);

在close方法中,使用2个参数,其中fd参数值必须为open方法所使用的回调函数中返回的文件描述符或openSync返回的描述符,callback参数为一个可选参数,用于指定关闭文件操作结束时执行的回调函数,在回调函数中使用一个参数,参数值为关闭文件操作失败时触发的错误对象。那我们现在可以在我们的根目录下创建a.txt文件并在其中写入 “我喜爱编程”字符串,然后关闭a.txt文件。

var fs = require('fs');
var buf = new Buffer("我喜爱编程");
fs.open('./a.txt','wx',function(err,fd){
    fs.write(fd,buf,0,15,0,function(err,written,buffer){
        if(err) {
            console.log("写文件操作失败");
        }else {
            console.log("写文件操作成功");
            fs.close(fd);
        }
    });
});

运行下可以看到在根目录下生成a.txt文件,内容如下:

我喜爱编程;

同步方式的close方法的使用方式如下所示:

fs.closeSync(fd);

使用一个参数,参数值为open方法所使用的回调函数中返回的文件描述符或openSync方法返回的文件描述符。

在使用write方法或writeSync方法在文件中写入数据时,操作系统的做法是首先将该部分数据读到内存中,再把数据写到文件中,当数据读完时并不代表数据已经写完,因为还有部分数据有可能留在内存缓冲区中,这时如果我们调用close方法或closeSync方法关闭文件,那么这部分的数据就会丢失,这时我们可以使用fs模块中的fsync方法对文件进行同步操作,即将内存缓冲区中的剩余数据全部写入文件,该方法使用如下:

fs.fsync(fd,[callback]);

代码可以如下:

var fs = require('fs');
var buf = new Buffer("我喜爱编程");
fs.open('./a.txt','w',function(err,fd){
    fs.write(fd,buf,0,15,0,function(err,written,buffer){
        if(err) {
            console.log("写文件操作失败");
        }else {
            console.log("写文件操作成功");
            fs.fsync(fd);
            fs.close(fd);
        }
    });
});

四:创建与读取目录

4.1 创建目录;

在fs模块中,可以使用mkdir方法创建目录,使用方法如下:

fs.mkdir(path,[mode],callback);

其中path参数与callback参数为必须输入的参数,[mode]为可选参数,path参数用于指定需要被创建的目录的完整路径及目录名;mode参数值用于指定该目录的权限,默认值为0777(表示任何人可读写该目录);callback参数用于指定创建目录操作完毕时调用的回调函数,该回调函数中使用一个参数,参数值为创建目录操作失败时触发的错误对象。

代码如下:

var fs = require('fs');
fs.mkdir('./test',function(err){
    if(err) {
        console.log("创建目录操作失败");
    }else {
        console.log("创建目录操作成功");
    }
});

演示如下:

我们看到在我们的目录下 就创建了一个test目录;

在使用同步方式创建目录时,可以使用fs模块中的mkdirSync方法,该方法使用如下:fs.mkdirSync(path,[mode]);

上面2个参数的含义与mkdir中的path,[mode]参数的含义一样。

4.2读取目录;

在fs模块中,可以使用readdir方法读取目录,该方法使用如下:

fs.readdir(path,callback);

在readdir方法中,使用2个参数,其中path参数用于指定需要被读取的目录的完整路径及目录名,callback参数用于指定读取目录操作完毕时调用的回调函数,该回调函数如下所示:

Function(err,files){};

第一个参数为读取目录操作失败时触发的错误的对象,第二个参数值为一个数组,其中存放了读取到的文件中的所有文件名。

下面我们来演示下读取根目录下的所有文件的代码如下:

var fs = require('fs');
fs.readdir('./',function(err,files){
    if(err){
        console.log("读取目录操作失败");
    }else {
        console.log(files);
    }
});

演示如下:

在使用同步方式读取目录时,可以使用readdirSync方法,该方法使用如下:

var file = fs.readdirSync(path);

在readdirSync方法中,使用一个参数,用于指定需要读取的目录的完整路径及目录名,该方法返回被读取目录中所有文件名构成的数组;

五:查看与修改文件或目录的信息

1. 检查文件或目录是否存在。

在fs模块中,可以使用exists方法检查一个文件或目录是否存在,该方法的使用方式如下所示:

fs.exists(path,callback);

在exists方法中,可以使用两个参数,path参数用于指定需要被检查的文件或目录的完整路径及文件名或目录名,callback参数用于指定检查文件或目录信息操作完毕时执行的回调函数,如下:

Function(exists){}

在该回调函数中,使用一个参数,当文件或目录存在时,该参数值为true,否则为false。如下代码所示:

var fs = require('fs');
fs.exists('./message.txt',function(exists){
    if(exists) {
        console.log("该文件存在");
    }else {
        console.log("该文件不存在");
    }
});

如果根目录有message.txt文件的话,说明文件已存在,否则的话 不存在。

在使用同步方法检查文件是否存在时,可以使用fs模块中的existSync方法,该方法的使用方式如下:

var exists = fs.existsSync(path);

2. 获取文件或目录的绝对路径

在fs模块中,可以使用realpath方法获取一个文件或目录的绝对路径,该方法使用如下所示:

fs.realpath(path,[cache],callback);

path参数与callback参数为必须的,cache参数为可选的,其中path参数为需要查看的文件或目录的完整路径,cache参数值为一个对象,其中存放了一些预先指定的路径,callback参数用于指定获取文件或目录的绝对路径操作完毕时执行的回调函数,该回调函数使用两个参数,其中err参数值为获取文件或目录的绝对路径操作失败时触发的错误对象,resolvedPath参数值为获取到的文件或目录的绝对路径。

如下代码:

var fs = require('fs');
fs.realpath('./message.txt',function(err,resolvedPath){
    if(err) {
        console.log("获取文件失败");
    }else {
        console.log(resolvedPath);
    }
});

演示如下:

在使用同步方式获取文件或目录的绝对路径时,可以使用realpathSync方法,该方法使用如下所示:

var resolvedPath = fs.realpathSync(path,[cache]);

3. 修改文件或目录的读写权限

在fs模块中,可以使用chmod方法修改文件或目录的读写权限,该方法使用如下所示:

fs.chmod(path,mode,callback);

在chomd方法中,可以使用三个参数,path参数用于指定需要被修改读写权限的文件的完整路径及文件或目录名,mode参数用于指定修改后的文件或目录读写权限;callback参数用于指定修改文件读写权限操作完成后执行的回调函数,该回调函数使用一个参数,参数值为修改文件读写权限操作失败的触发错误对象。

如下代码:

var fs = require('fs');
// 0600代表所有者可读写,其他人没有任何权限
fs.chmod('./message1.txt',0600,function(err){
    if(err) {
        console.log("修改文件权限操作失败");
    }else {
        console.log("修改文件权限操作成功");
    }
});

// 0644代表所有者可读写,其他人只读
fs.chmod('./message2.txt',0644,function(err){
    if(err) {
        console.log("修改文件权限操作失败");
    }else {
        console.log("修改文件权限操作成功");
    }
});

// 0755代表所有者所有权限,其他所有人可读和执行
fs.chmod('./message3.txt',0755,function(err){
    if(err) {
        console.log("修改文件权限操作失败");
    }else {
        console.log("修改文件权限操作成功");
    }
});

// 0740代表所有者所有权限,所有者所在的组只读
fs.chmod('./message4.txt',0740,function(err){
    if(err) {
        console.log("修改文件权限操作失败");
    }else {
        console.log("修改文件权限操作成功");
    }
});

在使用同步方式修改文件或目录的读写权限时,可以使用fs模块中的chmodSync方法,使用方法如下:

fs.chmodSync(path,mode);

原文地址:https://www.cnblogs.com/tugenhua0707/p/4472535.html