video2commons/frontend/upload.py
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
#
# Copyright (C) 2015-2016 Zhuyifei1999
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>
#
import os
import re
import uuid
import shutil
from flask import request, jsonify
RE_CONTENT_RANGE = re.compile(r'^bytes (\d+)-(\d+)/(\d+)$')
RE_ALLOWED_FILEKEYS = re.compile(r'^[a-zA-Z0-9-]+$')
class WrongOffset(Exception):
def __init__(self, offset):
super(WrongOffset, self).__init__(str(offset))
self.offset = offset
def getpath(digest):
return os.path.join(os.path.dirname(os.path.realpath(__file__)),
'static/uploads', digest)
def stat(permpath):
return os.path.getsize(permpath)
# Flask endpoint
def upload():
f = request.files['file']
assert f, "Where's my file?"
filekey = request.form.get('filekey') or str(uuid.uuid1())
assert RE_ALLOWED_FILEKEYS.match('filekey'), 'Unacceptable file key'
permpath = getpath(filekey)
content_range = (f.headers.get('Content-Range') or
request.headers.get('Content-Range'))
if content_range:
result, kwargs = handle_chunked(f, permpath, content_range)
else:
result, kwargs = handle_full(f, permpath)
kwargs['filekey'] = filekey
return jsonify(result=result, **kwargs)
# Flask endpoint
def status():
permpath = getpath(request.form['filekey'])
return jsonify(offset=stat(permpath))
def handle_full(f, permpath):
f.save(permpath)
return 'Success', {}
def handle_chunked(f, permpath, content_range):
try:
content_range = RE_CONTENT_RANGE.match(content_range)
assert content_range, 'Invalid content range!'
cr1, cr2, cr3 = [int(content_range.group(i)) for i in range(1, 4)]
if os.path.isfile(permpath):
size = stat(permpath)
else:
size = 0
if size != cr1:
raise WrongOffset(size)
with open(permpath, 'ab') as dest:
shutil.copyfileobj(f, dest)
except WrongOffset as e:
size = e.offset
else:
size = stat(permpath)
if size < cr3:
return 'Continue', {'offset': size}
elif size > cr3:
raise RuntimeError('What?! Uploaded file is larger than '
'what it is supposed to be?')
return 'Success', {}