Skip to content

Commit

Permalink
Added customizable upload_url and upload_folder properties
Browse files Browse the repository at this point in the history
  • Loading branch information
matthoskins1980 committed Feb 5, 2016
1 parent f00a9a9 commit 476b943
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 66 deletions.
6 changes: 3 additions & 3 deletions demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
import os

app = Flask(__name__)
tm = tus_manager(app)
tm = tus_manager(app, upload_url='/newsflow/api/file-upload')

@app.route("/demo")
def demo():
return render_template("demo.html")
return render_template("demo.html", upload_url = tm.upload_url )

# serve the uploaded files
@app.route('/uploads/<path:filename>', methods=['GET'])
def download(filename):
uploads = os.path.join(app.root_path, app.config['TUS_UPLOADSDIR'])
uploads = os.path.join(app.root_path, tm.upload_folder)
return send_from_directory(directory=uploads, filename=filename)

if __name__ == '__main__':
Expand Down
107 changes: 46 additions & 61 deletions flask_tus.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,28 @@

class tus_manager(object):

def __init__(self, app=None):
def __init__(self, app=None, upload_url='/file-upload', upload_folder='uploads/'):
self.app = app
if app is not None:
self.init_app(app)
self.init_app(app, upload_url, upload_folder)

def init_app(self, app):
app.config.setdefault('TUS_ROOTDIR', '/file-upload')
app.config.setdefault('TUS_UPLOADSDIR', 'uploads')
def init_app(self, app, upload_url='/file-upload', upload_folder='uploads/'):

self.upload_url = upload_url
self.upload_folder = upload_folder
self.tus_api_version = '1.0.0'
self.tus_api_version_supported = '1.0.0'
self.tus_api_extensions = ['creation', 'termination']
self.tus_max_file_size = 4294967296 # 4GByte

# Use the newstyle teardown_appcontext if it's available,
# otherwise fall back to the request context
if hasattr(app, 'teardown_appcontext'):
app.teardown_appcontext(self.teardown)
else:
app.teardown_request(self.teardown)

# register the two file upload endpoints
app.add_url_rule(app.config['TUS_ROOTDIR'], 'file-upload', self.tus_file_upload, methods=['OPTIONS', 'POST'])
app.add_url_rule('{}/<resource_id>'.format( app.config['TUS_ROOTDIR'] ), 'file-upload-chunk', self.tus_file_upload_chunk, methods=['HEAD', 'PATCH', 'DELETE'])
app.add_url_rule(self.upload_url, 'file-upload', self.tus_file_upload, methods=['OPTIONS', 'POST'])
app.add_url_rule('{}/<resource_id>'.format( self.upload_url ), 'file-upload-chunk', self.tus_file_upload_chunk, methods=['HEAD', 'PATCH', 'DELETE'])

# handle redis server connection
def redis_connect(self):
return redis.Redis()

# handle teardown of redis connection
def teardown(self, app):
# ctx = stack.top
# if hasattr(ctx, 'tus_redis'):
# ctx.tus_redis.disconnect()
pass

@property
def redis_connection(self):
ctx = stack.top
Expand All @@ -58,7 +44,6 @@ def redis_connection(self):
ctx.tus_redis = self.redis_connect()
return ctx.tus_redis


def tus_file_upload(self):
response = make_response("", 200)

Expand All @@ -80,7 +65,7 @@ def tus_file_upload(self):
metadata[key] = base64.b64decode(value)

file_size = int(request.headers.get("Upload-Length", "0"))
resource_id = uuid.uuid4()
resource_id = str(uuid.uuid4())

p = self.redis_connection.pipeline()
p.setex("file-uploads/{}/filename".format(resource_id), "{}".format(metadata.get("filename")), 3600)
Expand All @@ -90,7 +75,7 @@ def tus_file_upload(self):
p.execute()

try:
f = open("{}/{}".format(self.app.config['TUS_UPLOADSDIR'], resource_id), "wb")
f = open( os.path.join( self.upload_folder, resource_id ), "wb")
f.seek( file_size - 1)
f.write("\0")
f.close()
Expand All @@ -100,7 +85,7 @@ def tus_file_upload(self):
return response

response.status_code = 201
response.headers['Location'] = '{}/{}'.format(self.app.config['TUS_ROOTDIR'], resource_id)
response.headers['Location'] = '{}/{}'.format(self.upload_url, resource_id)
response.autocorrect_location_header = False

else:
Expand All @@ -116,7 +101,7 @@ def tus_file_upload_chunk(self, resource_id):
response.headers['Tus-Version'] = self.tus_api_version_supported

offset = self.redis_connection.get("file-uploads/{}/offset".format( resource_id ))
self.app.logger.info( offset );
upload_file_path = os.path.join( self.upload_folder, resource_id )

if request.method == 'HEAD':
offset = self.redis_connection.get("file-uploads/{}/offset".format( resource_id ))
Expand All @@ -132,7 +117,7 @@ def tus_file_upload_chunk(self, resource_id):
return response

if request.method == 'DELETE':
os.unlink("{}/{}".format( self.app.config['TUS_UPLOADSDIR'], resource_id))
os.unlink( upload_file_path )

p = self.redis_connection.pipeline()
p.delete("file-uploads/{}/filename".format(resource_id))
Expand All @@ -144,41 +129,41 @@ def tus_file_upload_chunk(self, resource_id):
response.status_code = 204
return respose

filename = self.redis_connection.get("file-uploads/{}/filename".format( resource_id ))
if filename is None or os.path.lexists("{}/{}".format(self.app.config['TUS_UPLOADSDIR'], resource_id )) is False:
response.status_code = 410
return response
if request.method == 'PATCH':
filename = self.redis_connection.get("file-uploads/{}/filename".format( resource_id ))
if filename is None or os.path.lexists( upload_file_path ) is False:
response.status_code = 410
return response

file_offset = int(request.headers.get("Upload-Offset", 0))
chunk_size = int(request.headers.get("Content-Length", 0))
file_size = int( self.redis_connection.get( "file-uploads/{}/file_size".format( resource_id )) )
file_offset = int(request.headers.get("Upload-Offset", 0))
chunk_size = int(request.headers.get("Content-Length", 0))
file_size = int( self.redis_connection.get( "file-uploads/{}/file_size".format( resource_id )) )

if request.headers.get("Upload-Offset") != self.redis_connection.get( "file-uploads/{}/offset".format( resource_id )): # check to make sure we're in sync
response.status_code = 409 # HTTP 409 Conflict
return response
if request.headers.get("Upload-Offset") != self.redis_connection.get( "file-uploads/{}/offset".format( resource_id )): # check to make sure we're in sync
response.status_code = 409 # HTTP 409 Conflict
return response

try:
f = open( "{}/{}".format(self.app.config['TUS_UPLOADSDIR'], resource_id), "r+b")
except IOError:
f = open( "{}/{}".format(self.app.config['TUS_UPLOADSDIR'], resource_id), "wb")
finally:
f.seek( file_offset )
f.write(request.data)
f.close()

new_offset = self.redis_connection.incrby( "file-uploads/{}/offset".format( resource_id ), chunk_size)
response.headers['Upload-Offset'] = new_offset

if file_size == new_offset: # file transfer complete, rename from resource id to actual filename
filename_parts = os.path.splitext(filename)
counter = 1
while True:
if os.path.lexists( "{}/{}".format(self.app.config['TUS_UPLOADSDIR'], filename)):
filename = "{}{}.{}".format( filename_parts[0], filename_parts[1], counter )
counter += 1
else:
break

os.rename( "{}/{}".format( self.app.config['TUS_UPLOADSDIR'], resource_id ), "{}/{}".format( self.app.config['TUS_UPLOADSDIR'], filename ))
try:
f = open( upload_file_path, "r+b")
except IOError:
f = open( upload_file_path, "wb")
finally:
f.seek( file_offset )
f.write(request.data)
f.close()

return response
new_offset = self.redis_connection.incrby( "file-uploads/{}/offset".format( resource_id ), chunk_size)
response.headers['Upload-Offset'] = new_offset

if file_size == new_offset: # file transfer complete, rename from resource id to actual filename
filename_parts = os.path.splitext(filename)
counter = 1
while True:
if os.path.lexists( os.path.join( self.upload_folder, filename )):
filename = "{}{}.{}".format( filename_parts[0], filename_parts[1], counter )
counter += 1
else:
break

os.rename( upload_file_path, os.path.join( self.upload_folder, filename ))
return response
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

setup(
name='Flask-Tus',
version='0.1.0',
version='0.2.0',
url='http://github.com/matthoskins1980/Flask-Tus/',
license='MIT',
author='Matt Hoskins',
Expand Down
2 changes: 1 addition & 1 deletion templates/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ <h1>tus-js-client demo</h1>
</label>
</td>
<td>
<input type="text" id="endpoint" name="endpoint" value="/file-upload">
<input type="text" id="endpoint" name="endpoint" value="{{ upload_url }}">
</td>
</tr>
<tr>
Expand Down

0 comments on commit 476b943

Please sign in to comment.