1. response 要帶 expire header + cache control, 同時出現時 cache control 優先權高, 在 expire 之前瀏覽器不會來問 2. 每個 request 要帶上 Etag + Last-Modified header, 第二次 request 的時候 browser 會把這兩個資訊帶過來問, request with 'if-modified-since' header, 這也要處理var ext = path.extname(realPath); ext = ext ? ext.slice(1) : 'unknown'; if (ext.match(config.Expires.fileMatch)) { var expires = new Date(); expires.setTime(expires.getTime() + config.Expires.maxAge * 1000); response.setHeader("Expires", expires.toUTCString()); // HTTP 1.0 response.setHeader("Cache-Control", "max-age=" + config.Expires.maxAge); // HTTP 1.1 }3. 為所有 response 都加上 Last-Modified headerfs.stat(realPath, function (err, stat) { var lastModified = stat.mtime.toUTCString(); response.setHeader("Last-Modified", lastModified); });4. 檢查 If-Modified-Since headerif (request.headers[ifModifiedSince] && lastModified == request.headers[ifModifiedSince]) { response.writeHead(304, "Not Modified"); response.end(); }5. 加上 gzipvar 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); }6. range: bytes=0-99,从0到99之间的数据字节。 bytes=-100,文件的最后100个字节。 bytes=100-,第100个字节开始之后的所有字节。 bytes=0-99,200-299,从0到99之间的数据字节和200到299之间的数据字节。exports.parseRange = function (str, size) { if (str.indexOf(",") != -1) { return; } var range = str.split("-"), start = parseInt(range0], 10), end = parseInt(range[1], 10); // Case: -100 if (isNaN(start)) { start = size - end; end = size - 1; // Case: 100- } else if (isNaN(end)) { end = size - 1; } // Invalid if (isNaN(start) || isNaN(end) || start > end || end > size) { return; } return {start: start, end: end}; };var compressHandle = function (raw, statusCode, reasonPhrase) { var stream = raw; var acceptEncoding = request.headers['accept-encoding'] || ""; var matched = ext.match(config.Compress.match); if (matched && acceptEncoding.match(/\bgzip\b/)) { response.setHeader("Content-Encoding", "gzip"); stream = raw.pipe(zlib.createGzip()); } else if (matched && acceptEncoding.match(/\bdeflate\b/)) { response.setHeader("Content-Encoding", "deflate"); stream = raw.pipe(zlib.createDeflate()); } response.writeHead(statusCode, reasonPhrase); stream.pipe(response); }; if (request.headers["range"]) { var range = utils.parseRange(request.headers["range"], stats.size); if (range) { response.setHeader("Content-Range", "bytes " + range.start + "-" + range.end + "/" + stats.size); response.setHeader("Content-Length", (range.end - range.start + 1)); var raw = fs.createReadStream(realPath, {"start": range.start, "end": range.end}); compressHandle(raw, 206, "Partial Content"); } else { response.removeHeader("Content-Length"); response.writeHead(416, "Request Range Not Satisfiable"); response.end(); } } else { var raw = fs.createReadStream(realPath); compressHandle(raw, 200, "Ok"); }test with curl:curl --header "Range:0-20" -i http://localhost:8000/index.html
3/12/2013
用NodeJS打造你的静态文件服务器 - CNode
用NodeJS打造你的静态文件服务器 - CNode
沒有留言:
張貼留言