How to use node to implement static file caching

How to use node to implement static file caching

cache

Browser caching is the process of the browser caching previously requested files so that they can be reused the next time they are accessed, saving bandwidth, increasing access speed, and reducing server pressure.

Cache location classification

Memory cache: Cache in memory, cleared when the browser is closed, usually stores some js libraries

Disk cache: The cache in the hard disk will not be cleared immediately after closing the browser. It usually stores large files, such as image resources and icon file libraries such as iconFont.

The difference between the two:

1. Reading speed: The memory cache caches the currently parsed files in the browser tab process, so that they can be read quickly the next time they are used;

Disk cache directly writes the cache to the hard disk file. Reading the cache requires I/O (reading) operations on the hard disk file stored in the cache, and then re-parsing the cache content. It is slower than memory cache.

2. Timeliness: The memory cache is stored in the process of the tab and is cleared when the tab is closed;

Disk cache: I don't know when it will be cleared (I hope someone can add some information)

3. Priority: memory cache is greater than disk cache

For large files, it is highly likely that they are not stored in memory, otherwise it is preferred. At present, it seems that the browser cache location cannot be controlled from the code perspective.

Cache settings header

cache-control

1. cache-control: max-age=10 // All requests sent again within 10 seconds will directly hit the strong cache, without making a request to the server, just read the browser cache
2. Cache-Control: no-cache //Disable forced caching. A request is made to the server each time, and the request will also be stored in the browser cache (basically cached by negotiation)
3. Cache-Control: no-store //Request the server every time and do not cache in the browser, which is equivalent to no cache
Copy code

Expires:

Compatible with lower version browsers, this is to set the absolute time, get the current time of the server and the current time of the browser for comparison (usually there is a deviation, which is a product of http1.0). When cache-control exists at the same time, cache-control has a higher priority.

  • last-modified: Used in pairs with If-Modified-Since when negotiating cache; the value of the If-Modified-Since request header corresponds to the value of the last-modified response header of the server. The server compares the modification time of the requested resource. If they are equal, the negotiation cache is hit and 304 is returned. The browser can read the cache.
  • Etag: resource identifier (also called fingerprint, usually an md5 value), used when negotiating cache, to compare whether the file has been modified; appears in pairs with If-None-Match

Etag is mainly used to solve some problems that Last-Modified cannot solve.

1. Some files may be changed periodically, but their content does not change (only the modification time changes). At this time, we do not want the client to think that the file has been modified and re-GET;

2. Some files are modified very frequently, such as in a time of less than a second (for example, modified N times within 1 second), and If-Modified-Since cannot check such fine details.

3. Some servers cannot accurately obtain the last modification time of the file;

4. Comparison of Etag priority when both Etag and Last-modify exist

Actual project: HTML does not allow caching. The JS referenced in HTML has a unique version number as a basis. When accessed again, the latest HTML is accessed. If the referenced JS or other file version numbers have not been modified, the local cache is used directly.

Node implements static file caching

File Structure

public corresponds to the static resources we use for testing

Strong Cache

Ideas

  • Creating a Service
  • The first request is to parse the request path, and fs.createReadStream().pipe() reads the file
  • Set the response header Cache-Control: max-age=10 to strengthen the relative time of the cache

Code Implementation

const http = require("http");
const url = require("url");
const fs = require("fs");
const path = require("path");
// Receive the file path and return the file type format corresponding to the file const mime = require("mime"); //npm i mime 

const server = http.createServer((req, res) => {
  let { pathname, query } = url.parse(req.url, true);
  //__dirname concatenates the absolute path of the folder where the current file is located with the requested pathlet filePath = path.join(__dirname, "public", pathname);
  console.log(req.url);//Refresh the page repeatedly within 10 seconds to see if it is continuously printed. If it hits the strong cache, it will print once every 10 seconds.// Set the header cache information. Within the specified cache time, the client does not need to initiate a request to the server again.res.setHeader("Cache-Control", "max-age=10"); // Set the cache duration; the priority of the current time of the request + max-age is higher than Expires.res.setHeader("Expires", new Date(Date.now() + 10).toUTCString()); // Compatible with lower version browsers, this is to set the absolute time, and get the current time of the server.// Get the request path to determine whether it is a file or a file directory.fs.stat(filePath, function (err, statObj) {
    // If the URL is parsed incorrectly, the request will fail and the corresponding URL resource will not be found. 404 will be returned.
    if (err) {
      res.statusCode = 404;
      res.end("NOT FOUND");
    } else {
      // If it is a file, use readable stream + pipe to read the file content, use mime to get the file content format, and set the encoding standard to utf-8
      if (statObj.isFile()) {
        fs.createReadStream(filePath).pipe(res);
        res.setHeader(
          "Content-Type",
          mime.getType(filePath) + ";charset=utf-8"
        );
      } else {
        // If it is a file directory, find the corresponding index.html in the directory
        let htmlPath = path.join(filePath, "index.html");
        // fs.access determines whether the concatenated path is accessible fs.access(htmlPath, function (err) {
          if (err) {
            // Inaccessible settings status code 404
            res.statusCode = 404;
            res.end("NOT FOUND");
          } else {
            //Accessible, use readable stream plus pipe to read file content fs.createReadStream(htmlPath).pipe(res);
            res.setHeader("Content-Type", "text/html;charset=utf-8");
          }
        });
      }
    }
  });
  // You can start the service by running nodemon cache.js at http://localhost:3000/ 
});
server.listen(3000, () => {
  console.log("server start 3000");
});

Effect display

Negotiation Cache

success

Ideas

  • Creating a Service
  • The first request is to parse the request path, and fs.createReadStream().pipe() reads the file
  • Set the response header Last-modified and return it to the browser
  • Request again, compare the browser if-last-modified and the current resource modification time, if they are equal, the negotiation cache is hit and the response code 304 is returned, otherwise the latest resource corresponding to the path is returned, and the response code 200

Code Implementation

const http = require("http");
const url = require("url");
const fs = require("fs");
const path = require("path");
const mime = require("mime");


  let filePath = path.join(__dirname, "public", pathname);
  console.log(req.url);
  fs.stat(filePath, function (err, statObj) {
    if (err) {
      res.statusCode = 404;
      res.end("NOT FOUND");
    } else {
      if (statObj.isFile()) {
        // Determine the change time of the file path requested by the browser through statObj.ctime
        const ctime = statObj.ctime.toUTCString();
        // Browser request header if-modified-since === the last modification time of the file. If the negotiation cache is hit, 304 is returned. Request resource in browser cache if (req.headers["if-modified-since"] === ctime) {
          res.statusCode = 304; //Go to the browser cache to find res.end(); //
        } else {
          // if-modified-since !== the last modification time of the file, the response header Last-modified sets the modification time of the current request file to the corresponding value of last-modify-since in the next browser request res.setHeader("Last-modified", ctime);
          fs.createReadStream(filePath).pipe(res);
          res.setHeader(
            "Content-Type",
            mime.getType(filePath) + ";charset=utf-8"
          );
        }
      } else {
        fs.access(htmlPath, function (err) {
          if (err) {
            // Inaccessible settings status code 404
            res.statusCode = 404;
            res.end("NOT FOUND");
          } else {
            fs.createReadStream(htmlPath).pipe(res);
            res.setHeader("Content-Type", "text/html;charset=utf-8");
          }
        });
      }
    }
  });
  // You can start the service by running nodemon cache2.js at http://localhost:3000/ 
});
server.listen(3000, () => {
  console.log("server start 3000");
});

Effect display

console.log(req.url); is executed every time the page is refreshed. The server is requested but the server returns 304. The negotiation cache is hit and the browser can directly read the cached resources.

success

Summarize

This is the end of this article on how to use node to implement static file caching. For more relevant node static file caching content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope everyone will support 123WORDPRESS.COM in the future!

<<:  Various problems encountered by novices when installing mysql into docker

>>:  Getting the creation time of a file under Linux and a practical tutorial

Recommend

Tomcat's class loading mechanism process and source code analysis

Table of contents Preface 1. Tomcat class loader ...

How to restore single table data using MySQL full database backup data

Preface When backing up the database, a full data...

Discussion on default margin and padding values ​​of common elements

Today we discussed the issue of what the margin v...

Using docker command does not require sudo

Because the docker daemon needs to bind to the ho...

Detailed explanation of how to implement secondary cache with MySQL and Redis

Redis Introduction Redis is completely open sourc...

React configuration px conversion rem method

Install related dependencies npm i lib-flexible -...

Several mistakes that JavaScript beginners often make

Table of contents Preface Confusing undefined and...

Dealing with the problem of notes details turning gray on web pages

1. In IE, if relative positioning is used, that is...

Ubuntu installs multiple versions of CUDA and switches at any time

I will not introduce what CUDA is, but will direc...

VUE implements timeline playback component

This article example shares the specific code of ...

An article tells you how to implement Vue front-end paging and back-end paging

Table of contents 1: Front-end handwritten paging...