在 Node.JS 中引用相对于应用程序根目录的文件的正确方法

Posted

技术标签:

【中文标题】在 Node.JS 中引用相对于应用程序根目录的文件的正确方法【英文标题】:Proper way to reference files relative to application root in Node.JS 【发布时间】:2012-10-14 15:43:29 【问题描述】:

我有一个在 AWS EC2 上的 Linux 上运行的 Node.JS 应用程序,它使用 fs 模块来读取 html 模板文件。这是应用程序的当前结构:

/server.js
/templates/my-template.html
/services/template-reading-service.js

HTML 模板将始终位于该位置,但是,模板阅读服务可能会移动到不同的位置(更深的子目录等)。从模板阅读服务中,我使用 fs.readFileSync()加载文件,如下所示:

var templateContent = fs.readFileSync('./templates/my-template.html', 'utf8');

这会引发以下错误:

Error: ENOENT, no such file or directory './templates/my-template.html'

我假设这是因为路径“./”解析到“/services/”目录而不是应用程序根目录。我也尝试将路径更改为“../templates/my-template.html”并且有效,但它似乎很脆弱,因为我想这只是相对于“向上一个目录”进行解析。如果我将 template-reading-service 移动到更深的子目录,那条路径就会中断。

那么,相对于应用程序的根目录引用文件的正确方法是什么?

【问题讨论】:

【参考方案1】:

对于 ES 模块,__dirname 不可用,因此请阅读this answer 并使用:

import  resolve, dirname, join  from 'path'
import  fileURLToPath  from 'url'
import fs from 'fs'

const relativePath = a => join(dirname(fileURLToPath(import.meta.url)), a)

const pathToFileInSameDirectory   = relativePath('./file.xyz')
const pathToFileInParentDirectory = relativePath('../file.xyz')

const content1 = fs.readFileSync(pathToFileInSameDirectory,   'utf8')
const content2 = fs.readFileSync(pathToFileInParentDirectory, 'utf8')

【讨论】:

【参考方案2】:

接受的答案是错误的。硬编码path.join(__dirname, '../templates') 将完全执行不需要的操作,如果service-XXX.js 文件移动到子位置(如给定示例services/template),则它会破坏主应用程序。

使用process.cwd() 将返回启动运行进程的文件的根路径(例如/Myuser/myproject/server.js 返回/Myuser/myproject/)。

这是问题Determine project root from a running node.js application的重复。

在这个问题上,__dirname 的答案得到了应得的适当鞭打。 路人,小心绿色标记。

【讨论】:

我在使用 app-root-path 包时过得很糟糕。有两种方法可以启动我的应用程序,node lib/my_app.js 或 bin/my_app.js。 path.join(__dirname) 我花了很多时间和代码修补 app-root-path 以跨平台工作,最终我切换到 path.join(__dirname) 只要项目文件不转移就可以工作。 对于任何将来查看这些答案的人:process.cwd 不是必然您正在寻找的答案。例如,在 Windows 上,如果您从 .bat 文件运行节点进程,则 process.cwd() 返回 .bat 文件的路径,而不是节点项目的根目录。我相信类似的规则也适用于 Linux 和 Mac。【参考方案3】:

试试

var templateContent = fs.readFileSync(path.join(__dirname, '../templates') + '/my-template.html', 'utf8');

【讨论】:

__dirname 实际上不是当前脚本/模块所在的路径吗?所以在我的例子中,当从 'template-reading-service.js' 中执行时,__dirname 不会解析为 '/services/'? @user1438940 我以为你在/server.js中执行文件读取代码。试试“../templates/my-template.html” 不,执行 fs.getFileSync 的代码在“template-reading-service.js”中。我不想使用“../”;这就是我的问题的全部意义。如果我使用“../”,然后我将“template-reading-service.js”从“/services/”目录移动到“/services/template/”,那么我的所有代码都会中断。或者,如果我想将模板目录的路径作为全局配置中的设置,然后由 20 个不同的其他服务使用,它们都位于目录结构中的不同位置,那么“../”不会随时随地工作 @user1438940 process.cwd() 返回执行的“js”文件的绝对路径。所以你可以在项目目录的根目录下运行一个js文件,剩下的在最后附上。 @InspiredJW 看起来您编辑了您的 __dirname 建议,但我最终将其用作我的解决方案。在全局配置中,我使用了以下内容,这对我来说仍然很糟糕......templates: directoryPath: path.join(__dirname, '../templates/') 【参考方案4】:

要获得节点进程运行所在目录的绝对文件系统路径,可以使用process.cwd()。所以假设您将 /server.js 作为一个将 /services/template-reading-service.js 实现为模块的进程运行,那么您可以从 /service/template-reading-service.js 执行以下操作:

var appRoot = process.cwd(),
    templateContent = fs.readFileSync(appRoot + '/templates/my-template.html', 'utf8');

如果这不起作用,那么您可能会将 /service/template-reading-service.js 作为单独的进程运行,在这种情况下,您需要让该进程的任何启动都通过它您要视为 primary 应用程序根目录的路径。例如,如果 /server.js 将 /service/template-reading-service.js 作为一个单独的进程启动,那么 /server.js 应该将它自己的 process.cwd 传递给它()。

【讨论】:

所以,我认为这可能对我有用,但它不适用于我的所有环境。在我的生产环境中,我使用的是forever,所以process.cwd 返回/ 对于任何将来查看这些答案的人:process.cwd 不是必然您正在寻找的答案。例如,在 Windows 上,如果您从 .bat 文件运行节点进程,则 process.cwd() 返回 .bat 文件的路径,而不是节点项目的根目录。我相信类似的规则也适用于 Linux 和 Mac。 process.cwd() 返回调用入口脚本的目录的路径,而不是定位文件的目录。所以,如果你从/var/www/project 调用index.jsprocess.cwd() 将是'/var/www/project',但如果你从/var/www 调用相同的脚本,它将是/var/www

以上是关于在 Node.JS 中引用相对于应用程序根目录的文件的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

node.js中static相对路径怎么写

Node.js的心脏-epoll

node.js 学习02

使用node.js编写前端代码资源文件.jpg.css报错404

`Node.js` 和/或其他 Javascript 分支相对于非 JS 框架(Rails、Django...)的性能、稳定性和速度

node rename绝对路径