Serving images

27 views
Skip to first unread message

Justin Henzie

unread,
Nov 3, 2008, 8:05:52 PM11/3/08
to Compojure
Hi all,

I am just starting with compojure so please forgive my naivety.

I am building a basic template that has a header which includes an
image.

I placed my images at the following path:

[APP_DIR]/public/images/imagename.jpg

but I have been unable to vend the image using any of the below:

[:img {:src "public/images/imagename.jpg"]

or

[:img {:src "/public/images/imagename.jpg"]

My assumption is that the APP_DIR represents root for the application
context and thus either of these paths should resolve to the named
image.

Any advice would be appreciated.

-Justin

James Reeves

unread,
Nov 3, 2008, 8:40:02 PM11/3/08
to Compojure
On Nov 4, 1:05 am, Justin Henzie <jhen...@mac.com> wrote:
> My assumption is that the APP_DIR represents root for the application
> context and thus either of these paths should resolve to the named
> image.

Compojure does not serve static files by default, as a developer may
not actually want any static files (such as in the case of a RESTful
web API). Similarly, it doesn't automatically deal out 404s, either.
Compojure doesn't make any assumptions about what you want to do; you
have to be explicit.

So in order to serve static files, you typically need to add in two
handlers. One to serve files, and the other to raise a 404 error if
the first handler fails:

(defservlet file-example
(GET "/public/*"
(or (serve-file (route :*)) :next))
(ANY "*"
(page-not-found)))

This example uses two functions: serve-file and page-not-found. Here
are their respective docstrings:

(page-not-found)
(page-not-found filename)
A shortcut to create a '404 Not Found' HTTP response.

(serve-file path)
(serve-file root path)
Attempts to serve up a static file from a directory, which defaults to
'./public'. Nil is returned if the file does not exist. If the file is
a
directory, the function looks for a file in the directory called
'index.*'.

So we try to serve a file, and if that comes up as nil, then we return
the keyword :next. This tells Compojure to keep looking for a matching
handler.

You don't have to use "/public/*" as the route to your static files,
either. You can just use "/*":

(defservlet file-example
; Put custom code here
(GET "/*"
(or (serve-file (route :*)) :next))
(ANY "*"
(page-not-found)))

Remember to ensure that the (ANY "*" ...) route is always at the
bottom. Handlers are matched in order, so you'll want your "catch-all"
404 handler to be matched last, when everything else fails.

Does that help answer your question?

- James

Justin Henzie

unread,
Nov 3, 2008, 10:49:24 PM11/3/08
to Compojure
James' explanation makes the mechanism abundantly clear to me now.
(See below)

One thing to be aware of is that using a handler like:

(GET "image/:image_name" (or (serve-file "public/
images" (route :image_name)) :next))

with a path like:

http://localhost/image/image.gif

will result in the next handler being invoked.

I am not sure why, 'yet', but the file extension (.gif) results in the
route name being unmatched. My solution to the problem for the time
being is to create a handler like:

(GET "/jpg/:image_name" (or (serve-file "public/images" (str
(route :image_name) ".jpg")) :next))

Hopefully this will be useful to other people new to compojure.

Cheers,

Justin

James Reeves

unread,
Nov 4, 2008, 4:29:37 AM11/4/08
to Compojure
On Nov 4, 3:49 am, Justin Henzie <jhen...@mac.com> wrote:
> One thing to be aware of is that using a handler like:
>
>     (GET "image/:image_name" (or (serve-file "public/
> images" (route :image_name)) :next))
>
> with a path like:
>
>    http://localhost/image/image.gif
>
> will result in the next handler being invoked.
>
> I am not sure why, 'yet', but the file extension (.gif) results in the
> route name being unmatched.

That's essentially because route symbols don't match slashes or dots
in the path. This is largely the case because that's the way Rails,
Merb and Sinatra do it, but it make some sense, as it allows you to
make the format independent of the route. For instance, IIRC, Rails
has two default routes:

":controller/:action/:id"
":controller/:action/:id.:format"

For static files, you'd be best off using the wildcard "*", which
matches everything.

(GET "/image/*"
(or (serve-file "public/images" (route :*))
:next))

Although I'm not sure why you don't just do:

(GET "/*"
(or (serve-file (route :*))
:next))

Is there a reason why you don't want a "catch-all" static file handler
like the above?

- James

Justin Henzie

unread,
Nov 4, 2008, 8:49:02 AM11/4/08
to Compojure
No reason why a single handler will not work for me, I am just working
my way through the project and disabusing myself of my preconceived
notions <smile/>

-jh

Daniel Renfer

unread,
Nov 4, 2008, 8:54:28 AM11/4/08
to comp...@googlegroups.com
-~---
>
>

This should really go into the readme/wiki. I was wondering how to do
this myself.

Justin Henzie

unread,
Nov 4, 2008, 10:24:58 AM11/4/08
to Compojure
Quick update:

The only way I have been able to get this to work is to use prefix'd
handlers.

Matching the entire route, for example

(GET "/*" (or (serve-file (route :*)) :next))

always results in page not found. I don't have enough knowledge of
clojure or compojure to figure out why this is the case yet but for
the time being I am able to vend images and javascript files using the
following handlers:

(GET "/images/*" (or (serve-file "public/
images" (route :*)) :next))

(GET "/js/*" (or (serve-file "public/js" (route :*)) :next))

I will work on trying to understand why this problem exists and
whether it is specific to my configuration.

-jh

On Nov 4, 1:29 am, James Reeves <weavejes...@googlemail.com> wrote:

James Reeves

unread,
Nov 4, 2008, 3:18:13 PM11/4/08
to Compojure
On Nov 4, 3:24 pm, Justin Henzie <jhen...@mac.com> wrote:
> Matching the entire route, for example
>
>     (GET "/*" (or (serve-file (route :*)) :next))
>
> always results in page not found.

Would it be possible for you to show me your servlet code?

- James
Reply all
Reply to author
Forward
0 new messages