Nodejs實現靜態伺服器
阿新 • • 發佈:2018-11-22
檔案結構:
assets資料夾存放請求的檔案
app.js檔案實現伺服器,執行時執行node app
瀏覽器在3000埠請求url為http://localhost:3000/app.html
//nodejs實現一個靜態檔案系統 var url = require('url'); var http = require('http'); var fs = require('fs'); //涉及檔案讀取 var path = require('path'); //涉及路徑處理 var mime = require('./mime').types; //引入mime型別 var config = require('./config'); var server = http.createServer(function(request, response) { //獲得請求url並得到最終的路徑 var pathname = url.parse(request.url).pathname; //檔案路徑字首為assets的具體路徑 var realPath = "/Users/cmy/Desktop/nodeserver/assets" + pathname; //通過fs模組的fs.access方法來判斷靜態檔案是否存在 fs.access(realPath, function (error) { if (!error) { //獲得副檔名,通過slice方法來剔除掉”.”,沒有後綴名的檔案認為是unknown var ext = path.extname(realPath); ext = ext ? ext.slice(1) : 'unknown'; //設定contentType型別,支援多種mime型別 var contentType = mime[ext] || 'text/plain'; response.setHeader('Content-Type',contentType); //設定快取支援和控制,提高效能,減少IO操作 fs.stat(realPath,function(err,stats){ //stats.mtime表示檔案最後一次被修改的時間。 var lastModified = stats.mtime.toUTCString(); var ifModifiedSince = 'If-Modified-Since'.toLowerCase(); response.setHeader('Last-Modified',lastModified); //為指定的某幾種字尾的檔案設定有效時間為1小時,在config中配置 if(ext.match(config.Expires.fileMatch)){ var expires = new Date(); //設定過期時間為當前時間加上maxage時間 //toUTCString() 方法可根據世界時 (UTC) 把 Date 物件轉換為字串,並返回結果。 //setTime和getTime都是以毫秒數設定或獲取時間 //max-age的單位是秒 expires.setTime(expires.getTime() + config.Expires.maxAge * 1000); response.setHeader("Expires", expires.toUTCString()); response.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge); } //判斷請求頭部的是否存在ifModifiedSince,並判斷lastModified是否等於ifModifiedSince //如果相等則說明檔案沒有修改,返回304 if(request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]){ response.writeHead(304,'Not Modified'); response.end(); }else{ //如果修改過則讀取檔案 fs.readFile(realPath, "binary", function(err, file) { if (err) { response.writeHead(500, {'Content-Type': 'text/plain'}); response.end(err); } else { response.writeHead(200, {'Content-Type': 'text/html'}); //response.write首次被呼叫時,會發送緩衝的響應頭資訊和響應主體的第一塊資料到客戶端 response.write(file, "binary"); response.end(); } }); } }); }else{ //如果檔案不存在返回404 response.writeHead(404, {'Content-Type': 'text/plain'}); response.write("This request URL " + pathname + " was not found on this server."); response.end(); } }); }); server.listen(3000); console.log('listenning at 3000');
新增gzip壓縮功能和路徑判斷補充功能的app.js
var url = require('url'); var http = require('http'); var fs = require('fs'); //涉及檔案讀取 var path = require('path'); //涉及路徑處理 var mime = require('./mime').types; //引入mime型別 var config = require('./config'); var zlib = require("zlib"); //使用gzip壓縮,引入原生模組zlib var server = http.createServer(function(request, response) { //獲得請求url並得到最終的路徑 var pathname = url.parse(request.url).pathname; ///結尾的請求,自動新增上”index.html if (pathname.slice(-1) === "/") { pathname = pathname + config.Welcome.file; } //使用normalize方法來處理掉不正常的/ var realPath = path.join("/Users/cmy/Desktop/nodeserver/assets", path.normalize(pathname.replace(/\.\./g, ""))); var pathHandle = function (realPath) { //使用fs.stat處理路徑 fs.stat(realPath, function (err, stats) { if (err) { response.writeHead(404, "Not Found", {'Content-Type': 'text/plain'}); response.write("This request URL " + pathname + " was not found on this server."); response.end(); } else { //如果請求的路徑沒有以/結尾,需要做判斷,看路徑是目錄還是檔案 if (stats.isDirectory()) { //如果是目錄則新增上/和index.html realPath = path.join(realPath, "/", config.Welcome.file); pathHandle(realPath); } else { //獲得副檔名,通過slice方法來剔除掉”.”,沒有後綴名的檔案認為是unknown var ext = path.extname(realPath); ext = ext ? ext.slice(1) : 'unknown'; var contentType = mime[ext] || "text/plain"; response.setHeader("Content-Type", contentType); //設定快取支援和控制,提高效能,減少IO操作 var lastModified = stats.mtime.toUTCString(); var ifModifiedSince = "If-Modified-Since".toLowerCase(); response.setHeader("Last-Modified", lastModified); if (ext.match(config.Expires.fileMatch)) { var expires = new Date(); expires.setTime(expires.getTime() + config.Expires.maxAge * 1000); response.setHeader("Expires", expires.toUTCString()); response.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge); } //判斷請求頭部的是否存在ifModifiedSince,並判斷lastModified是否等於ifModifiedSince //如果相等則說明檔案沒有修改,返回304 if (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) { response.writeHead(304, "Not Modified"); response.end(); } else { //如果修改過則讀取檔案 //為了防止大檔案,也為了滿足zlib模組的呼叫模式,將讀取檔案改為流的形式進行讀取。 //對於支援壓縮的檔案格式以及瀏覽器端接受gzip或deflate壓縮,我們呼叫壓縮。 //若不,則管道方式轉發給response。 var raw = fs.createReadStream(realPath); var acceptEncoding = request.headers['accept-encoding'] || ""; var matched = ext.match(config.Compress.match); if (matched && acceptEncoding.match(/\bgzip\b/)) { response.writeHead(200, "Ok", {'Content-Encoding': 'gzip'}); raw.pipe(zlib.createGzip()).pipe(response); } else if (matched && acceptEncoding.match(/\bdeflate\b/)) { response.writeHead(200, "Ok", {'Content-Encoding': 'deflate'}); raw.pipe(zlib.createDeflate()).pipe(response); } else { response.writeHead(200, "Ok"); raw.pipe(response); } } } } }); }; pathHandle(realPath); //路徑處理函式 }); server.listen(3000); console.log('listenning at 3000');
config.js 和 mime.js
//指定字尾檔案和過期日期,config.js exports.Expires = { fileMatch: /^(gif|png|jpg|js|css)$/ig, maxAge: 60*60 } //對於圖片一類的檔案,不需要進行gzip壓縮,只壓縮三種檔案 exports.Compress = { match: /css|js|html/ig }; //使用者請求了一個目錄路徑,而且沒有帶上/。那麼我們為其新增上/index.html,再重新做解析 exports.Welcome = { file: "index.html" }; //content-type對應型別,mime.js exports.types = { "css": "text/css", "gif": "image/gif", "html": "text/html", "ico": "image/x-icon", "jpeg": "image/jpeg", "jpg": "image/jpeg", "js": "text/javascript", "json": "application/json", "pdf": "application/pdf", "png": "image/png", "svg": "image/svg+xml", "swf": "application/x-shockwave-flash", "tiff": "image/tiff", "txt": "text/plain", "wav": "audio/x-wav", "wma": "audio/x-ms-wma", "wmv": "video/x-ms-wmv", "xml": "text/xml" };