fs.readFile()在第一次读取后,是否会将文件的内容缓存在服务器的内存中?

4
我想知道以下代码是否会在读取一次文件后将其内容缓存到服务器内存中。我之所以这样问是因为我不想每次用户请求页面时都重新读取文件,我更喜欢在第一次读取后将其缓存。
fs.exists(fileName, function (exists) {
        if (!exists) {
            console.log("== 404 error");
            resp.writeHead(404, {'Content-Type': 'text/html'});
            resp.end(pageError);
            return;
        }

        fs.readFile(fileName, 'utf8', function (err, data) {
            if (err) {
                resp.writeHead(404, {"Content-Type": "text/html"});
                resp.end(pageError);
                return;
            }

            var contentType = getContentType(req.url);
            var mimeType = mimeTypes[contentType];

            resp.writeHead(200, {"Content-Type": mimeType});
            resp.end(data);
        });
    });

注意 ** 我只想知道如何使用内部的Node.js模块来实现这个(不需要使用express)


1
只需将数据保存在变量中即可。为什么不在服务器启动时进行,而非在用户请求页面时进行呢? - George
这是一种有效的方式吗? - C. Ols
一个有效的方式是什么?您可以在启动时只读取一次readFile,并在每个页面请求上提供该数据。请记住,如果您不再次读取文件,则不会反映对文件所做的更改。 - George
3个回答

4
您不应该使用已过时的fs.exists(),而是应该使用fs.stat()来检查文件是否存在。如果您在检查存在性后要打开和读取文件,则只需使用fs.readFile()并根据情况处理传递的错误即可。这在 fs.access() 的文档中有说明,但对于fs.stat()也适用。以下是 Node.js 文档中的摘录:

在调用 fs.open()、fs.readFile() 或 fs.writeFile() 之前使用 fs.access() 检查文件的可访问性不是推荐的做法。这样做会引入竞争条件,因为其他进程可能会在两次调用之间更改文件的状态。相反,用户代码应直接打开/读取/写入文件,并处理引发的错误(如果文件不可访问)。

fs.readFile() 不会为您执行任何缓存操作,这是您需要自己创建/管理的。下面的示例显示如何使用 JS 对象作为字典来创建文件缓存,以便通过文件名索引文件内容。重要的是要注意,您不应该将大量数据放入fileCache对象中,而应该将其用于许多较小的文件。 fileCache 只需要在 getFileFromCache() 的范围内,并且位于在运行时不会被垃圾回收的地方即可。
const fileCache = {}
const getFileFromCache = (filename, cb) => {
    if (fileCache[filename]) {
        return cb(null, fileCache[filename])
    }

    fs.readFile(filename, 'utf8', (err, data) => {
      if (err) {
        return cb(err)
      }

      fileCache[filename] = data
      return cb(null, data)     
    })
}

不仅 fs.exists() 已经过时,而且在这种类型的代码中也没有理由使用 fs.stat()。只需执行 fs.readFile() 并在文件不存在时处理错误。更高效且不容易发生竞争条件。 - jfriend00
@jfriend00 当然,我把它留在那里,如果 OP 以某种原因想要访问文件统计数据。我会更新我的答案来解决这个问题。 - peteb
@jfriend00 更新了我的答案,以更好地解决你提出的竞态条件问题。 - peteb

0

如果您想要在多个模块中访问文件数据,可以将文件数据存储在变量或全局变量中(使用global.<varname> = <filedata>)。

当然,正如George Cambpell所说,对文件的任何修改都不会被程序注意到,因为它不会重新读取文件。

因此,我会这样做:

function sendResponse(data) {
    let contentType = getContentType(req.url);
    let mimeType = mimeTypes[contentType];
    resp.writeHead(200, {"Content-Type": mimeType});
    resp.end(data);
}

if(global.fileData) {
    return sendResponse(global.fileData);
}

fs.readFile(fileName, 'utf8', function (err, data) {
    if (err) {
        resp.writeHead(404, {"Content-Type": "text/html"});
        resp.end(pageError);
        return;
    }

    global.fileData = data;
    sendResponse(global.fileData);
});

第一次global.fileData将为空,因此您将继续使用fs.readfile,将文件内容存储在global.fileData中,并发送响应。
第二次global.fileData将包含内容,因此您只需使用该内容发送响应,而不会再次读取文件。
有关更多信息,请参阅官方NodeJS文档:https://nodejs.org/api/globals.html#globals_global

另一件事情是用fs.access或fs.stat(我通常使用fs.access)替换fs.exists,因为exists方法已被弃用。
https://nodejs.org/api/fs.html#fs_fs_stat_path_callback
https://nodejs.org/api/fs.html#fs_fs_access_path_mode_callback

愉快的编码!


0

fs.readFile()在第一次读取后会将文件内容缓存到服务器内存中吗?

不会。 fs.readFile()本身不会进行缓存。

但是,底层操作系统会进行文件缓存,只要没有太多其他文件活动导致缓存读取被刷新,那么操作系统可能会从本地内存缓存中获取文件的第二、第三次读取。

如果您想自己确保缓存,则应该在第一次读取后自己存储内容,然后从那时起,您可以使用先前读取的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接