davegson
davegson

Reputation: 8331

server side image caching

Our webapp will perform API calls to retrieve images. These images can be cached for a reasonable amount of time, as stated in the terms, so we don't have to hit up the other website's servers on every page request.

I am new to caching, but I just watched some Railscasts about some different methods. page caching, fragment caching, dynamic page caching. They all help decrease the requests and therefor the speed of my app.

But how would I go by 'caching' images from another site on my servers? What is the correct way to do so? Are there any functions to serve this purpose?

My attempt

would be to add a timestamp, cache_outdates_at to my Image model and set it one month into the future. Whenever the image get's loaded again it would update the timestamp.

Then I'd add a cronjob to check for outdated images and then delete them.

Upvotes: 4

Views: 2772

Answers (3)

mikdiet
mikdiet

Reputation: 10018

I can share with you my own approach to cache external images on my side. Also, when I solve such task, I needed to update images on my side when underlying external image updated.

External service use e-tags for signing images, so I use this feature next way.

I have Photo model which stores external_url and etag, as well as some info about image (image name for carrier wave in my case).

So, I have image_url, received from some API, and I want to upload this image on my server, and don't want to load up external server.

      photo = Photo.find_or_initialize_by(external_url: image_url)
      img_responce = Faraday.get do |req|
        req.url image_url
        if photo.etag
          req.headers['If-None-Match'] = photo.etag
        end
      end

      case img_responce.status
      when 200
        # This mean, image changes (or new)
        photo.update external_url: image_url,
                     etag: img_responce.headers['etag'],
                     image: StringIO.new(img_responce.body)
      when 304
        # This mean, image doesn't change
      end 

Upvotes: 0

Paritosh Piplewar
Paritosh Piplewar

Reputation: 8122

Nice Question. If you look the problem outside the box, it is fairly easy.

When you retrieve 3rd party server image, just copy that images and put it in your server or in CDN. And, use it. For this you have to setup some method something like

# app/model/image.rb
def image_link(name_of_image)
 if File.exist? "/some/#{name_of_image}.jpg"
    path = "/some/#{name_of_image}.jpg"
 else
  external_image_link = external_api_call(name_of_image)
  DownloadImageWorker.perform_async(external_image_link) // perform it asynchronously
  external_image_link 
 end

Upvotes: 3

Alex Trebek
Alex Trebek

Reputation: 917

I suggest that you not clutter your Rails application with code to cache off-site assets. Instead, use an HTTP proxy that is built for the job, such as Squid. Take a look at this example Squid configuration that caches off-site videos.

So, your webapp just needs to rewrite URLs, say, by appending cache.example.com, or whatever the hostname of your caching server will be, to the URLs it generates. Set up a wildcard DNS entry so that *.cache.example.com all point to your Squid server. Squid can be configured to rewrite the domain, fetch the remote asset, and serve it.

All of the cache management, including ejection of stale items, would be taken care of for you automatically by Squid. As a bonus, performance is likely to be better than on Rails.

Upvotes: 1

Related Questions