implement file size upload limit

This commit is contained in:
Jakob Ketterl 2021-04-29 18:18:18 +02:00
parent 7115d5c951
commit af553c422d
4 changed files with 75 additions and 17 deletions

View File

@ -6,6 +6,7 @@ $.fn.imageUpload = function() {
var originalUrl = $img.prop('src');
var $input = $(this).find('input');
var id = $input.prop('id');
var maxSize = $(this).data('max-size');
$uploadButton.click(function(){
$uploadButton.prop('disabled', true);
var input = document.createElement('input');
@ -14,9 +15,20 @@ $.fn.imageUpload = function() {
input.onchange = function(e) {
var reader = new FileReader()
// TODO: implement file size check
reader.readAsArrayBuffer(e.target.files[0]);
reader.onprogress = function(e) {
if (e.loaded > maxSize) {
console.error('maximum file size exceeded, aborting file upload');
$uploadButton.prop('disabled', false);
reader.abort();
}
};
reader.onload = function(e) {
if (e.loaded > maxSize) {
console.error('maximum file size exceeded, aborting file upload');
$uploadButton.prop('disabled', false);
return;
}
$.ajax({
url: '/imageupload?id=' + id,
type: 'POST',

View File

@ -1,6 +1,10 @@
from datetime import datetime, timezone
class BodySizeError(Exception):
pass
class Controller(object):
def __init__(self, handler, request, options):
self.handler = handler
@ -33,10 +37,12 @@ class Controller(object):
self.handler.send_header("Location", location)
self.handler.end_headers()
def get_body(self):
def get_body(self, max_size=None):
if "Content-Length" not in self.handler.headers:
return None
length = int(self.handler.headers["Content-Length"])
if max_size is not None and length > max_size:
raise BodySizeError("HTTP body exceeds maximum allowed size")
return self.handler.rfile.read(length)
def handle_request(self):

View File

@ -1,11 +1,20 @@
from owrx.controllers import BodySizeError
from owrx.controllers.assets import AssetsController
from owrx.controllers.admin import AuthorizationMixin
from owrx.config.core import CoreConfig
from owrx.form.input.gfx import AvatarInput, TopPhotoInput
import uuid
import json
class ImageUploadController(AuthorizationMixin, AssetsController):
# max upload filesizes
max_sizes = {
# not the best idea to instantiate inputs, but i didn't want to duplicate the sizes here
"receiver_avatar": AvatarInput("id", "label").getMaxSize(),
"receiver_top_photo": TopPhotoInput("id", "label").getMaxSize(),
}
def __init__(self, handler, request, options):
super().__init__(handler, request, options)
self.file = request.query["file"][0] if "file" in request.query else None
@ -29,22 +38,37 @@ class ImageUploadController(AuthorizationMixin, AssetsController):
def processImage(self):
if "id" not in self.request.query:
self.send_response("{}", content_type="application/json", code=400)
# TODO: limit file size
contents = self.get_body()
self.send_json_response({"error": "missing id"}, code=400)
return
file_id = self.request.query["id"][0]
if file_id not in ImageUploadController.max_sizes:
self.send_json_response({"error": "unexpected image id"}, code=400)
return
try:
contents = self.get_body(ImageUploadController.max_sizes[file_id])
except BodySizeError:
self.send_json_response({"error": "file size too large"}, code=400)
return
filetype = None
if self._is_png(contents):
filetype = "png"
if self._is_jpg(contents):
filetype = "jpg"
if filetype is None:
self.send_response("{}", content_type="application/json", code=400)
else:
self.file = "{id}-{uuid}.{ext}".format(
id=self.request.query["id"][0],
uuid=uuid.uuid4().hex,
ext=filetype,
)
with open(self.getFilePath(), "wb") as f:
f.write(contents)
self.send_response(json.dumps({"file": self.file}), content_type="application/json")
self.send_json_response({"error": "unsupported file type"}, code=400)
return
self.file = "{id}-{uuid}.{ext}".format(
id=file_id,
uuid=uuid.uuid4().hex,
ext=filetype,
)
with open(self.getFilePath(), "wb") as f:
f.write(contents)
self.send_json_response({"file": self.file}, code=200)
def send_json_response(self, obj, code):
self.send_response(json.dumps(obj), code=code, content_type="application/json")

View File

@ -7,7 +7,7 @@ class ImageInput(Input, metaclass=ABCMeta):
def render_input(self, value, errors):
# TODO display errors
return """
<div class="imageupload">
<div class="imageupload" data-max-size="{maxsize}">
<input type="hidden" id="{id}" name="{id}">
<div class="image-container">
<img class="{classes}" src="{url}" alt="{label}"/>
@ -16,7 +16,11 @@ class ImageInput(Input, metaclass=ABCMeta):
<button type="button" class="btn btn-secondary restore">Restore original image</button>
</div>
""".format(
id=self.id, label=self.label, url=self.cachebuster(self.getUrl()), classes=" ".join(self.getImgClasses())
id=self.id,
label=self.label,
url=self.cachebuster(self.getUrl()),
classes=" ".join(self.getImgClasses()),
maxsize=self.getMaxSize(),
)
def cachebuster(self, url: str):
@ -34,6 +38,10 @@ class ImageInput(Input, metaclass=ABCMeta):
def getImgClasses(self) -> list:
pass
@abstractmethod
def getMaxSize(self) -> int:
pass
class AvatarInput(ImageInput):
def getUrl(self) -> str:
@ -42,6 +50,10 @@ class AvatarInput(ImageInput):
def getImgClasses(self) -> list:
return ["webrx-rx-avatar"]
def getMaxSize(self) -> int:
# 256 kB
return 250 * 1024
class TopPhotoInput(ImageInput):
def getUrl(self) -> str:
@ -49,3 +61,7 @@ class TopPhotoInput(ImageInput):
def getImgClasses(self) -> list:
return ["webrx-top-photo"]
def getMaxSize(self) -> int:
# 2 MB
return 2 * 1024 * 1024