Question

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.

Was it helpful?

Solution 2

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'})
])

OTHER TIPS

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.

StaticFileHandler gets is file name from the regex capturing group and the directory name from its path argument. It will work if you use /path/to/ as the path:

(r'/(foo\.json)', tornado.web.StaticFileHandler, {'path': '/path/to/'})

StaticFileHandler is designed for cases where URLs and filenames match; if you can't arrange to have this file available on disk under the same name you want to serve it as you'll have to use a custom handler.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top