Let tornado handle the file downloads

master
Gina Häußge 2013-09-02 13:18:45 +02:00
parent 928cfd3950
commit 928a8b3d9c
1 changed files with 16 additions and 32 deletions

View File

@ -2,10 +2,9 @@
__author__ = "Gina Häußge <osd@foosel.net>" __author__ = "Gina Häußge <osd@foosel.net>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename, redirect
from werkzeug.datastructures import Headers
from sockjs.tornado import SockJSRouter, SockJSConnection from sockjs.tornado import SockJSRouter, SockJSConnection
from flask import Flask, request, render_template, jsonify, send_from_directory, url_for, current_app, session, abort, make_response, Response from flask import Flask, request, render_template, jsonify, send_from_directory, url_for, current_app, session, abort, make_response
from flask.ext.login import LoginManager, login_user, logout_user, login_required, current_user from flask.ext.login import LoginManager, login_user, logout_user, login_required, current_user
from flask.ext.principal import Principal, Permission, RoleNeed, Identity, identity_changed, AnonymousIdentity, identity_loaded, UserNeed from flask.ext.principal import Principal, Permission, RoleNeed, Identity, identity_changed, AnonymousIdentity, identity_loaded, UserNeed
@ -347,8 +346,7 @@ def readGcodeFiles():
@app.route(BASEURL + "gcodefiles/<path:filename>", methods=["GET"]) @app.route(BASEURL + "gcodefiles/<path:filename>", methods=["GET"])
def readGcodeFile(filename): def readGcodeFile(filename):
path = os.path.join(settings().getBaseFolder("uploads"), filename) return redirectToTornado(request, "/downloads/gcode/" + filename)
return _streamBinaryFile(path, mimeType="text/plain", asAttachment=True)
@app.route(BASEURL + "gcodefiles/upload", methods=["POST"]) @app.route(BASEURL + "gcodefiles/upload", methods=["POST"])
@restricted_access @restricted_access
@ -517,9 +515,7 @@ def getTimelapseData():
@app.route(BASEURL + "timelapse/<filename>", methods=["GET"]) @app.route(BASEURL + "timelapse/<filename>", methods=["GET"])
def downloadTimelapse(filename): def downloadTimelapse(filename):
if util.isAllowedFile(filename, set(["mpg"])): return redirectToTornado(request, "/downloads/timelapse/" + filename)
path = os.path.join(settings().getBaseFolder("timelapse"), filename)
return _streamBinaryFile(path, mimeType="video/mpeg", asAttachment=True)
@app.route(BASEURL + "timelapse/<filename>", methods=["DELETE"]) @app.route(BASEURL + "timelapse/<filename>", methods=["DELETE"])
@restricted_access @restricted_access
@ -930,29 +926,15 @@ def load_user(id):
return userManager.findUser(id) return userManager.findUser(id)
return users.DummyUser() return users.DummyUser()
def _streamBinaryFile(filename, mimeType=None, asAttachment=False, attachmentFilename=None): def redirectToTornado(request, target):
if not os.path.exists(filename) or not os.path.isfile(filename): requestUrl = request.url
return app.make_response(("No such file: %s" % filename, 404, [])) appBaseUrl = requestUrl[:requestUrl.find(BASEURL)]
def generator(path, chunkSize=4096): redirectUrl = appBaseUrl + target
with open(path, "rb") as f: if "?" in requestUrl:
while True: fragment = requestUrl[requestUrl.rfind("?"):]
data = f.read(chunkSize) redirectUrl += fragment
if not data: return redirect(redirectUrl)
break
yield data
headers = Headers()
if asAttachment:
if attachmentFilename is None:
attachmentFilename = os.path.basename(filename)
headers.add("Content-Disposition", "attachment", filename=attachmentFilename)
headers.add("Content-Length", os.stat(filename).st_size)
if mimeType is None:
mimeType = "application/octet-stream"
return Response(generator(filename), mimetype=mimeType, headers=headers)
#~~ startup code #~~ startup code
class Server(): class Server():
@ -980,7 +962,7 @@ class Server():
from tornado.wsgi import WSGIContainer from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado.web import Application, FallbackHandler from tornado.web import Application, FallbackHandler, StaticFileHandler
debug = self._debug debug = self._debug
@ -1029,7 +1011,9 @@ class Server():
self._router = SockJSRouter(self._createSocketConnection, "/sockjs") self._router = SockJSRouter(self._createSocketConnection, "/sockjs")
self._tornado_app = Application(self._router.urls + [ self._tornado_app = Application(self._router.urls + [
(".*", FallbackHandler, {"fallback": WSGIContainer(app)}) (r"/downloads/timelapse/([^/]*\.mpg)", StaticFileHandler, {"path": settings().getBaseFolder("timelapse")}),
(r"/downloads/gcode/([^/]*\.(gco|gcode))", StaticFileHandler, {"path": settings().getBaseFolder("uploads")}),
(r".*", FallbackHandler, {"fallback": WSGIContainer(app)})
]) ])
self._server = HTTPServer(self._tornado_app) self._server = HTTPServer(self._tornado_app)
self._server.listen(self._port, address=self._host) self._server.listen(self._port, address=self._host)