Search code examples
node.jsimageexpressbrowser-cache

Use nodejs express to respond with existing image file that the browser can cache?


I have a small webapp built with nodejs and express (among other things) that has a route to resize images on the fly (using sharp). The route looks like this:

router.get('/image/:options/:basedir/:dir/:img', utilitiesController.getOptimizedImage);

In the utilities controller, I have the getOptimizedImage function checking for the existing image, returning the existing image content if it exists, or if it doesn't, performing some image processing tasks, then returning the resulting image.

exports.getOptimizedImage = async (req, res) => {
    // Parse options from request...

    // first, check to see if resized version exists
    fs.readFile(processedImgPath, function (err, processedImg) {

        if (err) {
        //console.log('File does not yet exist.');

            // If resized version doesn't exist, check for original
            fs.readFile(originImgPath, function (err, originImg) {

                if (err) {
                     // If origin image doesn't exist, return 400.

                } else if (w || h) {
                    // If origin image does exist, process it...
                    // Once it's processed, return the processed image
                    res.end(newImg);
                    return;  
                }
            }

        } else {
            //
            res.end(processedImg);
            //res.redirect(existingFileUrl);
            return;
        }
    } 
}

This code works. I can request something like so:

<img src="http://example.com/image/w800/foo/bar/imagename.jpg">

...and it returns the resized image as expected. The issue seems to be that because of the way the image is returned using res.end(), the browser cache (testing in Chrome) doesn't ever store the image, so reloading the page downloads the image fresh instead of loading it from memory or disk.

I can alternatively use res.redirect() to send back the url of the existing processed image file, which will be cached on refresh, but that feels like the wrong way to do this, since it ultimately doubles all the image requests using the image processing path.

I don't want to process the images prior to request; I'm specifically looking to only process the images the first time they're requested, then store a processed version to reference each consecutive time. I'm open to alternatives within these constraints, but hoping someone can explain how I might leverage browser caching with my current structure?


Solution

  • You should add http headers for caching before any res.end, the example below will set the Expires time to 1 day (86400000ms).

    res.set({
        "Cache-Control": "public, max-age=86400",
        "Expires": new Date(Date.now() + 86400000).toUTCString()
    })