You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Node.js Express中实现支持浏览器缓存的已有图片返回?

Got it, let's figure out how to add proper browser caching to your Express image route. This will make repeat requests load faster and reduce your server's bandwidth usage. Here's a step-by-step implementation tailored to your setup:

Core Implementation: Update getOptimizedImage with Caching Logic

We'll leverage HTTP cache headers (Cache-Control, ETag, Last-Modified) and handle 304 Not Modified responses to let browsers reuse cached images.

First, make sure you're using the promise-based fs methods for cleaner async/await code:

const fs = require('fs').promises;
const path = require('path');
const sharp = require('sharp');

Then update your controller function:

async function getOptimizedImage(req, res) {
  const { options, basedir, dir, img } = req.params;
  // Define the path to your preprocessed/cached image
  const cachedImagePath = path.join(__dirname, '../path/to/your/cache/dir', options, basedir, dir, img);

  try {
    // Check if the cached image exists
    await fs.access(cachedImagePath);

    // Get file stats to generate cache headers
    const fileStats = await fs.stat(cachedImagePath);
    const lastModified = fileStats.mtime.toUTCString();
    const etag = `"${fileStats.size}-${fileStats.mtime.getTime()}"`;

    // Set cache control: adjust max-age based on how often your images change
    // Example: 7 days (604800 seconds) – use longer for static images, shorter for dynamic ones
    res.setHeader('Cache-Control', 'public, max-age=604800');
    res.setHeader('Last-Modified', lastModified);
    res.setHeader('ETag', etag);

    // Check if browser already has a valid cache
    const clientModifiedSince = req.headers['if-modified-since'];
    const clientEtag = req.headers['if-none-match'];

    if (
      (clientModifiedSince && clientModifiedSince === lastModified) ||
      (clientEtag && clientEtag === etag)
    ) {
      // Image hasn't changed – tell browser to use cache
      return res.sendStatus(304);
    }

    // Send the cached image if cache is stale or missing
    res.sendFile(cachedImagePath);
  } catch (err) {
    // Cached image doesn't exist – process with Sharp
    const originalImagePath = path.join(__dirname, '../path/to/your/original/images', basedir, dir, img);

    try {
      // Parse your options (adjust this based on how your :options param is structured)
      const [widthStr, heightStr] = options.split(',');
      const width = parseInt(widthStr.split('-')[1]);
      const height = parseInt(heightStr.split('-')[1]);

      // Process the image with Sharp
      const processedImage = await sharp(originalImagePath)
        .resize(width, height)
        .toBuffer();

      // Create directory structure for cached image if needed
      await fs.mkdir(path.dirname(cachedImagePath), { recursive: true });
      // Save processed image to cache
      await fs.writeFile(cachedImagePath, processedImage);

      // Set cache headers for the newly generated image too
      const newFileStats = await fs.stat(cachedImagePath);
      res.setHeader('Cache-Control', 'public, max-age=604800');
      res.setHeader('Last-Modified', newFileStats.mtime.toUTCString());
      res.setHeader('ETag', `"${newFileStats.size}-${newFileStats.mtime.getTime()}"`);

      // Send the processed image
      res.type('image/jpeg').send(processedImage);
    } catch (processingErr) {
      res.status(404).send('Image not found or failed to process');
    }
  }
}

Key Details Explained

  • Cache-Control Header:
    • public allows the image to be cached by browsers, CDNs, and other intermediate caches.
    • max-age=604800 sets the cache duration to 7 days (in seconds). Tweak this based on your image update frequency – use 2592000 for 30 days if images rarely change.
  • ETag & Last-Modified: These headers act as unique identifiers for the image version. When the browser re-requests the image, it sends these values back in If-None-Match and If-Modified-Since headers. If they match the server's current values, we return a 304 Not Modified instead of re-sending the image.
  • 304 Response: This tells the browser "your cached version is still valid" – it skips downloading the image entirely, saving bandwidth and speeding up load times.

Additional Tips

  • Ensure Cache Directory Permissions: Make sure your Node.js process has write access to the cache directory you're using.
  • Cache Invalidation: If you ever need to update an image, delete the corresponding cached file – the next request will reprocess and cache the new version. Alternatively, include a content hash in your image URLs (e.g., image/w-200/h-300/myimg-v2.jpg) to force browsers to fetch the new version.
  • MIME Types: If you support multiple image formats (PNG, WebP), adjust res.type() to match the processed image's format.

内容的提问来源于stack exchange,提问作者spencerready

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:09:24