前言
相信絕大多數的前端和我一樣都習慣用相對路徑,因為在寫前端代碼的時候基本上都是用相對路徑,而很少使用絕對路徑,使用相對路徑在當使用nodejs的fs子產品讀取檔案時就有可能會給自己挖下這麼一個坑(經典的nodejs的路徑問題),問題如下:
問題
現在有一個demo,檔案目錄結構如下圖:
server.js的代碼為:
let http = require('http');
let fs = require('fs');
let path = require('path');
const PORT = ;
// web頁面的根路徑
// 注意:現在這裡是相對路徑
let PUBLIC_PATH = '../public';
http.createServer((req, res) => {
if (req.url === '/favicon.ico') {
res.end();
return;
}
let _path = '';
console.log(`請求路徑為:${req.url}`);
if (req.url === '/') {
_path = PUBLIC_PATH + '/home.html';
}else {
_path = PUBLIC_PATH + req.url;
}
sendFile(res, _path);
}).listen(PORT, () => {
console.log('Server listening on port 3000.');
});
function sendFile(res, path) {
fs.exists(path, (exist) => {
if (exist) {
fs.readFile(path, (err, file) => {
if (err) {
send404(res);
} else {
let mime = '';
if (path.indexOf('.html')) {
mime = 'text/html';
}else if (path.indexOf('.js')) {
mime = 'application/javascript';
}
res.writeHead(, {'Content-type': `${mime};chartset=utf-`});
console.log('status : 200');
res.end(file);
}
});
} else {
send404(res);
}
});
}
function send404(res) {
res.writeHead(, {'Content-type': 'text/plain'});
console.log('status : 404');
res.end('404');
}
注意這裡是以相對路徑去讀寫靜态資源:
// web頁面的根路徑
// 注意:現在這裡是相對路徑
let PUBLIC_PATH = '../public';
然後以以下指令啟動server.js并通路localhost:3000,列印結果為:
可以看到,正确通路到資源。
但是當以以下指令啟動server.js并通路localhost:3000,列印結果為:
可以看到,無法通路到資源。
然後改變:
// web頁面的根路徑
// 注意:現在這裡是相對路徑
let PUBLIC_PATH = './public';
然後以以下指令啟動server.js并通路localhost:3000,列印結果為:
可以看到,正确通路到資源。
顯而易見,fs子產品讀取檔案的相對路徑是以啟動server.js的位置為基準的,而不是以server.js檔案的位置。
這就是這篇文章所要講述的問題。
解決
很顯然,啟動腳本的方式可以有多種,是以nodejs官方推薦在使用fs子產品讀取檔案時使用絕對路徑,而不是相對路徑。
改變:
// web頁面的根路徑
// 注意:現在改為絕對路徑
let PUBLIC_PATH = path.resolve(__dirname, '../public');
經測試,無論在那個位置啟動server.js都可以通路到靜态資源。
路徑
仍然以上面demo的目錄結構(環境:window10;node v8.4.0),總結一些nodejs的常用路徑:
//擷取node.exe的絕對路徑
console.log(process.execPath);//D:\nodejs\node.exe
//存放目前檔案(即server.js)檔案夾的絕對路徑
console.log(__dirname);//D:\nodeTest\node_path\lib
//目前檔案(即server.js)的絕對路徑
console.log(__filename);//D:\nodeTest\node_path\lib\server.js
//從所傳入的檔案路徑(相對或絕對)中擷取存放傳入檔案的檔案夾的相對或絕對路徑
//(例如 傳入 public/home.html 則傳回的是public)
console.log(path.dirname(__filename));//D:\nodeTest\node_path\lib
//執行目前腳本(即server.js)的位置 (例如 在根目錄下執行 node ./xxx/xxx/a.js 則傳回的是根目錄位址 )
console.log(process.cwd());//D:\nodeTest\node_path\lib
//'a/b/c'和'../src' 組合而成的絕對路徑 檔案或檔案夾都行
//例如 console.log(path.resolve('a/b/c', '../src'));//D:\nodeTest\node_path\lib\a\b\src
console.log(path.resolve(__dirname, '../public'));//D:\nodeTest\node_path\public
//'a/b/c'和'../src'組成的相對路徑
//console.log(path.join('a/b/c', '../src'));//a\b\src
console.log(path.join(__dirname, '../public'));//D:\nodeTest\node_path\public
//相當于path.resolve(__dirname, '../public/home.html')或path.join(__dirname, '../public/home.html')
//但傳入的必須是檔案路徑,而不是檔案夾路徑,而且當檔案不存在時會抛出異常
console.log(require.resolve('../public/home.html'));//D:\nodeTest\node_path\public\home.html