Sending binary files shouldn't be part of the project boiler plate, I think it should be included in the framework itself. This is a simplified version I made from Flask's send_file
import os
import mimetypes
from sanic.response import HTTPResponse
def sendfile(location, mimetype=None, add_etags=True):
headers = {}
filename = os.path.split(location)[-1]
with open(location, 'rb') as ins_file:
out_stream = ins_file.read()
if add_etags:
headers['ETag'] = '{}-{}-{}'.format(
int(os.path.getmtime(location)),
hex(os.path.getsize(location)),
adler32(location.encode('utf-8')) & 0xffffffff)
mimetype = mimetype or mimetypes.guess_type(filename)[0] or 'text/plain'
return HTTPResponse(status=200,
headers=headers,
content_type=mimetype,
body_bytes=out_stream)
im working on this, i want to make it similar to how express handles static files.
app.static('/static'(uri for static files), "./images"(directory with static files))
@sm5art I'm not a big fan of flask's way of defining static files. I find this syntax to be much simpler :). If it could support serving both directories and single files that would be awesome.
I think it good to take in account the case of serving files from root path. For example file ./static/robots.txt should be served not as /static/robotx.txt, but as /robots.txt.
And if we configure it like
app.static('/', './static')
app.route('*')(handler)
It should first look for file in static folder and if it isn't exist use handler to handle this url.
And if you need standard binding of /static/* urls to ./static folder you can just configure it like:
app.route(...)
app.route(...)
app.static('/static', './static')
So static handler should not throw 404 error for not existing files, but work like middleware and handle next route.
Added in static file support based on the pull request, but I didn't see this comment and don't want to close this until we can support missing static files falling back to routes. I'm currently thinking the middleware might be a good solution, but adding middleware by default slows requests. Maybe we could make optional middleware to support it?
I don't think it's a good idea at all to handle static files with sanic in production. We can mention in docs to use middleware only in dev enviroment like:
if settigs.DEBUG:
app.static.override(folder='./static', url_prefix='/')
So it will have no affect on production perfomance.
And we can add to docs some proxy servers configs into deploying section. E.g. for nginx:
location / {
try_files $uri @proxy;
}
location @proxy {
proxy_pass http://127.0.0.1:8000;
}
I also think that it's good idea to have both static routes and overriding middleware. Because they serve some different purposes. Static route is good to maintain independent apps based on blueprints, they can contain they own handlers, assets (and maybe templates in the future). And middleware can be used at the project level.
@imbolc I think it looks pretty good -- the sendfile method could easily be written in async by reading the file with a CHUNKSIZE and yielding the response.
Is somebody working on streaming responses?
Nginx has a lot of optimization like sendfile support. I don't think it impossible to achieve similar performance with sanic. I'm just not sure it's worth the effort.
And it's common for python async frameworks to use nginx for static on production. Aiohttp docs (it has stream support): "The best way to handle static in production is setting up reverse proxy like NGINX or using CDN services.". Tornado docs: "In production, you probably want to serve static files from a more optimized static file server like nginx."
https://github.com/channelcat/sanic/pull/104 has been merged. Does this issue still needs to be opened?
@simnalamburt good call, closing with #104
Most helpful comment
im working on this, i want to make it similar to how express handles static files.
app.static('/static'(uri for static files), "./images"(directory with static files))