#!/usr/bin/env python # -*- 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/> # from __future__ import absolute_import 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', {}