Search code examples
lispcommon-lispcaveman2

Caveman2 How to serve a file dynamically?


I would like to be able to serve a file with Caveman2. The reason I want a way to return it from a controller and not make a static route available to anyone is to be able to only serve the file to authenticated users that have the permission level required.

I found this answer, including it here for reference: Serving Static Files with Common Lisp , and while I found that my quicklisp had already included usocket and flexi-streams, I did not follow exactly how to do it. Which socket should I use? Is that the correct way of doing it? Is that what's supposed to be done from Caveman2

From here How do I force files to open in the browser instead of downloading (PDF)? it seems that for the browser to handle the response correctly I need to set certain headers.

So this seems to be just a question of how to return a file to the browser.

Since this seems like such a standard operation I image there's a standard way of doing it and I don't want to reinvent the wheel over here, just couldn't find it in the docs...

Thanks in advance


Solution

  • The way to solve the problem was to return a sequence. First read the file into a sequence and then return it.

    As included in the question, the correct headers need to be set.

    1. Content-Type is set like this: (setf (getf (caveman2:response-headers caveman2:*response*) :content-type) "image/png"). This example has "image/png" but you can set it up depending on whatever the file is.
    2. `Content-Disposition is set similarly
      (setf
       (getf (caveman2:response-headers caveman2:*response*) :content-disposition)
       "attachment; filename=\"filename.png\"")
    

    You can replace attachment in the code above with inline and it will prompt for a file download instead of an in browser render.

    Finally, and the main answer to the question, is to return a sequence of the byte-vector.

    (alexandria:read-stream-content-into-byte-vector STREAM)
    

    In my case, since I'm using mito-attachment, I'm including the code here for reference:

    (alexandria:read-stream-content-into-byte-vector 
      (mito-attachment:get-object 
        (mito:find-dao 'class-name :file-key file-key)))