Let tornado handle the file downloads
parent
928cfd3950
commit
928a8b3d9c
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue