Problem with caches_page of action sending data

Hi all

My current projekt needs a feature where my client can upload some
pictures. For simplicity I just want to store them in the DB because
this is easier to maintain (backup, restore etc.) and it’s really only a
few dozens compressed files.

I looked into the Agile Rails Web Dev 2nd Ed book and figured out the
following action which sends the data of the picture (stored in the DB)
to the user as an image:

class PicturesController < ApplicationController

def receive
@pic = Picture.find(params[:id])
send_data(@pic.data,
:filename => @pic.name,
:type => @pic.content_type,
:disposition => “inline”)
end
end

This works very well so far. To speed up the page, I want to cache this
action, so I use
caches_page :receive
in the PicturesController. And now I run in serious problems… Let me
show you.

  1. I open a page which has an HTML image tag to a picture, let’s say it
    uses <img href="/public/pictures/receive/55" … />
  2. This loads the data slowly from the DB as expected and shows it as a
    picture in my page.
  3. I reload the same page (no complete reload of all stuff, only by
    placing the cursor in the address bar of Firefox and hitting Enter)
  4. The image is again very slow - it seems that it is again loaded from
    the DB and not from the cache!
  5. I take a look at the cache: hrm, there definitely is a cached version
    of my pic #55, but it’s called 55.png (although it was a JPG file), so
    the complete path is /public/pictures/receive/55.png
  6. No reason the file is loaded from the cache again, because my HTML
    img tag expects a file called only 55, not 55.png!
  7. I open the image directly in the browser by pointing it to
    /public/pictures/receive/55.png
  8. This displays the image as expected, and again it is very slow
    (because it’s loaded from the DB, nobody’s surprised here)
  9. I reload the image (again by placing the cursor in Firefox’
    addressbar and hitting Enter)
  10. And now that seems very strange - no image is displayed, but a very
    long text with very confusing characters and stuff! What happened here??
  11. I look into the cache - and what do I see? Now there’s another
    cached file, named 55.html!
  12. I go back to the page I opened in the first step, and now the image
    is displayed very fastly, so everything seems right from this
    perspective: the cached image is loaded from the cache.
  13. But this all is far away from being bug-free code! The image now is
    only shown as an image because (I guess) the browser expects an image
    when using an img tag, so it doesn’t care about wrong headers or
    something like that, and just interprets the incoming stuff from 55.html
    as an image. (And 55.html is only successfully loaded by the img tag
    because the webserver points to .html automatically when no extension is
    given in this case).

So what’s the problem here? I have adapted the receive() method from the
Agile Rails Web Dev book, so it seems this code is not really functional
for real-world-applications? Anyone can fix it? I just want the action
pictures/receive/55 to cache the image as /public/pictures/55 with no
png or html extension…

Thanks a lot for your help! :slight_smile:
Joshua

I’m sorry I made some small errors in the post above:

  1. No wonder the file is loaded from the cache again, because my HTML
    img tag expects a file called only 55, not 55.png!
  2. I open the image directly in the browser by pointing it to
    /public/pictures/receive/55 (not 55.png)

Joshua M. wrote:

I’m sorry I made some small errors in the post above:

  1. No wonder the file is loaded from the cache again, because my HTML
    img tag expects a file called only 55, not 55.png!
  2. I open the image directly in the browser by pointing it to
    /public/pictures/receive/55 (not 55.png)

All page_cache’d files must have an extension, and one isnt present,
rails assumes what it is and uses that. This is because rails doesnt
serve page caches, only the webserver does. And the webserver has to
know what content type to send the file as.

You can use a custom route to create the proper url and caching
filename.

map.picture ‘pictures/recieve/:id/image.jpg’,
:controller => ‘pictures’,
:action => ‘recieve’

img = Picture.find(1)
image_tag picture_url(:id => img)

Or if you are on edge rails, and your images are not always the same
format, you can separate parameters with dots:

map.picture ‘pictures/recieve/:id.:format’,
:controller => ‘pictures’,
:action => ‘recieve’

img = Picture.find(1)
image_tag picture_url(:id => img, :format => img.format)