前端最基础的就是 HTML+CSS+Javascript
。掌握了这三门技术就算入门,但也仅仅是入门,现在前端开发的定义已经远远不止这些。前端小课堂(HTML/CSS/JS
),本着提升技术水平,打牢基础知识的中心思想,我们开课啦(每周四)。
使用 Node.js 很大一部分的使用场景是,写个小脚本批量处理一下重复无聊的任务。比如说:
- 获取所有 json 内容,然后过滤出想要的数据。
- 把所有图片压缩一下,然后放入压缩包中。
- 把字体文件提取压缩一下。
- 把所有文件重命名一下。
这些任务中我们都需要访问文件系统,之前在浏览器环境中其实是没有文件系统概念的,今天我们来学一哈。
Node.js 文件模块
我们可以使用 fs
来操作文件系统。
const fs = require('fs');
回调方式
所有的文件系统操作都具有同步的(10.x 有,历史也有)、回调的(10.x 有,历史也有)、以及基于 promise(12.x 开始就有了,历史没有) 的形式。
同步
同步的形式会阻止 Node.js 事件循环和进一步的 JavaScript 执行,直到操作完成。
异常会被立即地抛出,可以使用 try…catch
处理,也可以冒泡。
const fs = require('fs');
try {
fs.unlinkSync('文件');
console.log('已成功地删除文件');
} catch (err) {
// 处理错误
}
异步
异步的形式总是把完成回调作为其最后一个参数。
传给完成回调的参数取决于具体方法,但第一个参数总是预留给异常。 如果操作被成功地完成,则第一个参数会为 null
或 undefined
。
const fs = require('fs');
fs.unlink('文件', (err) => {
if (err) throw err;
console.log('已成功地删除文件');
});
promise
基于 Promise 的操作会返回 Promise
(当异步操作完成时会被解决)。
const fs = require('fs/promises');
(async function(path) {
try {
await fs.unlink(path);
console.log(`已成功地删除文件 ${path}`);
} catch (error) {
console.error('出错:', error.message);
}
})('文件');
文件路径
支持 字符串(常用Path库就对了)、Buffer(为了兼容)、URL(需要 new URL('file:///C:/'
) 为入参。可以用相对路径 (process.cwd()
可以查看当前路径),绝对路径。
字符串形式
在 Windows 上,Node.js 遵循独立驱动器工作目录的概念。 当使用没有反斜杠的驱动器路径时,可以观察到此行为。 例如, fs.readdirSync('C:\\')
可能会返回与 fs.readdirSync('C:')
不同的结果。 详见此 MSDN 页面。
一般我们使用 Path 库:
- 可以直接看一下最终地址
- 可以正确使用当前系统的路径分隔符,Unix系统是"/",Windows系统是""。
URL 形式
// 在 Windows 上:
// - 带有主机名的 WHATWG 文件的 URL 会转换为 UNC 路径。
// file://主机名/文件 => 主机名文件
fs.readFileSync(new URL('file://主机名/文件'));
// - 带有驱动器号的 WHATWG 文件的 URL 会转换为绝对路径。
// file:///C:/文件 => C:文件
fs.readFileSync(new URL('file:///C:/文件'));
API
目录 dir 类
fs.readdirSync()
同步获取目录fs.readdir()
异步获取目录
当调用 fs.readdir()
或 fs.readdirSync()
(withFileTypes
选项设置为 true
)时,则生成的数组会使用 fs.Dirent
对象(而不是字符串或 Buffer
)填充。
fs.mkdir(path[, options], callback)
异步地创建目录。回调会传入可能的异常、以及创建的第一个目录的路径(如果 recursive
为 true
), (err, [path])
。可选的 options
参数可以是整数(指定 mode
(权限和粘滞位))、或对象(具有 mode
属性和 recursive
属性(指示是否要创建父目录))。 当 path
是已存在的目录时,调用 fs.mkdir()
仅在 recursive
为 false 时才会导致错误。fs.mkdirSync(path[, options])
同步地创建目录。 返回 undefined
,或创建的第一个目录的路径(如果 recursive
为 true
)。 这是 fs.mkdir()
的同步版本。
监听 fs.FSWatcher 类
fs.watch()
每当指定监视的文件被修改时,会触发 'change'
事件。
// 使用 fs.watch()监听器的示例。
fs.watch('./tmp', {
encoding: 'buffer'
}, (eventType, filename) => {
if (filename) {
console.log(filename);
}
});
读取流 fs.ReadStream 类
使用 fs.createReadStream()
函数创建并返回的 fs.ReadStream
实例。
fs.createReadStream(path[, options])
path
<string> | <Buffer> | <URL>- 返回: <fs.ReadStream> 参见可读流。
写入流 fs.WriteStream 类
使用 fs.createWriteStream()
函数创建并返回的 fs.WriteStream
实例。
fs.createWriteStream(path[, options])
path
<string> | <Buffer> | <URL>- 返回: <fs.WriteStream> 参见可写流。
,.
文件信息 fs.Stats 类
fs.Stats
对象提供了关于文件的信息。在遍历的时候我们会判断文件类型,如果是文件就打开,如果是目录就递归
从 fs.stat()
、fs.lstat()
、fs.fstat()
、以及它们的同步方法返回的对象都是此类型。 如果传给这些方法的 options
中的 bigint
为 true,则数值会是 bigint
型而不是 number
型,并且该对象还会包含额外的纳秒级精度的属性(以 Ns
为后缀)。
stats.isDirectory()
如果fs.Stats
对象描述文件系统目录,则返回true
。stats.isFile()
如果fs.Stats
对象描述普通的文件,则返回true
。stats.size
文件的大小(以字节为单位)。stats.mtime
表明上次修改此文件的时间戳。stats.mtimeMs
表明上次修改此文件的时间戳,以 POSIX 纪元以来的毫秒数表示。stats.birthtime
表示此文件的创建时间的时间戳。stats.birthtimeMs
表明此文件的创建时间的时间戳,以 POSIX 纪元以来的毫秒数表示。
文件属性的时间值#
atimeMs
、mtimeMs
、ctimeMs
和birthtimeMs
属性是保存相应时间(以毫秒为单位)的数值。 它们的精度取决于平台。 当将bigint: true
传给生成该对象的方法时,属性将会是 bigint 型,否则它们将会是数字型。
atimeNs
、mtimeNs
、ctimeNs
和birthtimeNs
属性是保存相应时间(以纳秒为单位)的 bigint。 仅当将bigint: true
传给生成该对象的方法时,它们才会出现。 它们的精度取决于平台。
atime
、mtime
、ctime
和birthtime
是对应时间的Date
对象。Date
值和数值没有关联性。 赋值新的数值、或者改变Date
的值,都将不会影响到对应的属性。stat 对象中的时间具有以下语义:
atime
"访问时间" - 上次访问文件数据的时间。由mknod(2)
、utimes(2)
和read(2)
系统调用更改。mtime
"修改时间" - 上次修改文件数据的时间。由mknod(2)
、utimes(2)
和write(2)
系统调用更改。ctime
"更改时间" - 上次更改文件状态(修改索引节点数据)的时间。由chmod(2)
、chown(2)
、link(2)
、mknod(2)
、rename(2)
、unlink(2)
、utimes(2)
、read(2)
和write(2)
系统调用更改。birthtime
"创建时间" - 创建文件的时间。当创建文件时设置一次。 在不支持创建时间的文件系统上,该字段可能改为保存ctime
或1970-01-01T00:00Z
(即 Unix 纪元时间戳0
)。 在这种情况下,该值可能大于atime
或mtime
。 在 Darwin 和其他的 FreeBSD 衍生系统上,也可能使用utimes(2)
系统调用将atime
显式地设置为比birthtime
更早的值。在 Node.js 0.12 之前,在 Windows 系统上
ctime
保存birthtime
。 从 0.12 开始,ctime
不再是“创建时间”,而在 Unix 系统上则从来都不是。
获取权限访问 fs.access、fs.accessSync
除了判断权限,还可以判断是否存在。不过我们一般可以直接 fs.open()
直接去处理 err 。通常,仅在不直接使用文件时(例如当其可访问性是来自其他进程的信号时),才检查文件的可访问性。
在 Windows 上,目录上的访问控制策略(ACL)可能会限制对文件或目录的访问。 但是, fs.access()
函数不检查 ACL,因此即使 ACL 限制用户读取或写入,也可能报告路径是可访问的。
fs.access(path[, mode], callback)
path
<string> | <Buffer> | <URL>mode
<integer> 默认值:fs.constants.F_OK
。callback
<Function>err
<Error>
测试用户对 path
指定的文件或目录的权限。 mode
参数是一个可选的整数,指定要执行的可访问性检查。 查看文件可访问性的常量了解 mode
的可选值。 可以创建由两个或更多个值按位或组成的掩码(例如 fs.constants.W_OK | fs.constants.R_OK
)。
文件
fs.open(path[, flags[, mode]], callback)
异步地打开文件。
fs.openSync(path[, flags, mode])
同步打开文件fs.read(fd, buffer, offset, length, position, callback)
从fd
指定的文件中读取数据,写入到buffer
中。
fs.readSync(fd, buffer, offset, length, position)
同步版本,从指定 fs 中读取数据
fs.readFile(path[, options], callback)
异步地读取文件的全部内容。
fs.readFileSync(path[, options])
同步版本,读取文件全部内容fs.appendFile(path, data[, options], callback)
异步地追加数据到文件,如果文件尚不存在则创建文件。data
可以是字符串或Buffer
。
fs.appendFileSync(path, data[, options])
同步地将数据追加到文件,如果文件尚不存在则创建该文件。data
可以是字符串或Buffer
。// 直接针对文件追加 fs.appendFileSync('文件.txt', '追加的数据', 'utf8'); // 先打开文件,然后针对文件描述符追加 fd = fs.openSync('文件.txt', 'a'); fs.appendFileSync(fd, '追加的数据', 'utf8');
fs.copyFile(src, dest[, mode], callback)
异步地将src
拷贝到dest
。 默认情况下,如果dest
已经存在,则覆盖它。 除了可能的异常,回调函数没有其他参数。 Node.js 不保证拷贝操作的原子性。 如果在打开目标文件用于写入后发生错误,则 Node.js 将尝试删除目标文件。fs.copyFileSync(src, dest[, mode])
同步fs.rename(oldPath, newPath, callback)
异步地把oldPath
文件重命名为newPath
提供的路径名。 如果newPath
已存在,则覆盖它。 除了可能的异常,完成回调没有其他参数。
fs.renameSync(oldPath, newPath)
同步版本,文件重命名fs.write(fd, buffer[, offset[, length[, position]]], callback)
写入buffer
到fd
指定的文件。不等待回调就对同一个文件多次使用fs.write()
是不安全的。 对于这种情况,建议使用fs.createWriteStream()
。
示例
获取目录下所有图片,并上传到服务器
const FormData = require('form-data');
const fetch = require('node-fetch');
var fs = require('fs');
var path = require('path');
var dirPath = 'cdn-transform'
var filePath = path.resolve(`./node-upload-img/${dirPath}`);
//文件遍历方法
function fileDisplay(filePath){
fs.readdir(filePath,function(err,files){
if(err){
console.warn(err)
}else{
files.forEach(function(filename){
var filedir = path.join(filePath, filename);
var buffer = fs.readFileSync(filedir)
var formData = new FormData();
var fileName = `${filename}`
var url = `www.lilnong.top/upload/fe-up/${dirPath}/${fileName}`;
formData.append('file', buffer, `${fileName}`);
fetch('http://www.lilnong.top/upload',{
headers: formData.getHeaders(),
method: 'post',
body: formData
})
.then(v=>v.text())
.then(v=>{
console.log('http://www.lilnong.top/upload', url, v)
})
})
}
});
}
fileDisplay(filePath)
递归遍历所有json
const readDir = (entry, paths = []) => {
const dirInfo = fs.readdirSync(entry);
dirInfo.forEach(item=>{
const location = path.join(entry,item);
const info = fs.statSync(location);
if(info.isDirectory()){
console.log(`dir:${location}`);
readDir(location, [item]);
}else{
if(/.json$/.test(location)){
readFile(location, paths)
}
}
})
}
console.log('__dirname', __dirname)
readDir(__dirname);
function readFile(path, pathKey){
return console.log(path, pathKey);
})
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。