Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can Tornado serve a single static file at an arbitrary location?

I am developing a simple web app with Tornado. It serves some dynamic files and some static ones. The dynamic ones are not a problem, but I am having trouble serving a static file. What I am looking to do is to serve the file /path/to/foo.json when the /foo.json URL is accessed.

Note that /path/to/foo.json is outside the document root. In Apache I would just set up an Alias. With Tornado I have:

app = tornado.web.Application([
    (r'/dynamic\.html', MyService, dict(param = 12345)),
    (r'/(foo\.json)', tornado.web.StaticFileHandler, {'path': '/path/to/foo.json'})
    ])

I added the regex group operator () to satisfy Tornado, which threw an exception otherwise. But now, when I access /foo.json, I get a 404: File Not Found.

Tests reveal that Tornado is attempting to use the path provided as a root directory to which it appends foo.json, implying my file could be found if it were at /path/to/foo.json/foo.json. Close, but not quite.

I suppose I could shorten my path to simply "/path/to", which will trigger a fetch of /path/to/foo.json upon the /foo.json URL, but this forces me to use the same name in the URL as on the filesystem. How can I just do a simple, arbitrary, URL to file mapping?

I have done some research on this, reading the documentation for tornado.web.Application and tornado.web.StaticFilehandler, plus some other SO questions. Nothing is quite my use case.

like image 296
Randall Cook Avatar asked Jan 21 '14 02:01

Randall Cook


2 Answers

StaticFileHandler expects two arguments, so if you want a single url (/foo.json) to be mapped to your file path you can use:

app = tornado.web.Application([
(r'/foo.json()', tornado.web.StaticFileHandler, {'path': '/path/to/foo.json'})
])

The regex will match /foo.json and send the empty capture group (), which will cause the filepath to be used as is. When the capture group is not empty, /path/to/foo.json will be treated as a directory /path/to/foo.json/, and the handler will try to match whatever is within the capture group to a file name in that directory.

like image 188
Mark Avatar answered Oct 24 '22 19:10

Mark


Something like this should work:

import os
import tornado.ioloop
import tornado.web


class MyFileHandler(tornado.web.StaticFileHandler):
    def initialize(self, path):
        self.dirname, self.filename = os.path.split(path)
        super(MyFileHandler, self).initialize(self.dirname)

    def get(self, path=None, include_body=True):
        # Ignore 'path'.
        super(MyFileHandler, self).get(self.filename, include_body)

app = tornado.web.Application([
    (r'/foo\.json', MyFileHandler, {'path': '/path/to/foo.json'})
])

app.listen(8888)
tornado.ioloop.IOLoop.current().start()

The URL pattern and the filename need not be related, you could do this and it would work just as well:

app = tornado.web.Application([
    (r'/jesse\.txt', MyFileHandler, {'path': '/path/to/foo.json'})
])
like image 21
A. Jesse Jiryu Davis Avatar answered Oct 24 '22 19:10

A. Jesse Jiryu Davis