implement file size upload limit
This commit is contained in:
parent
7115d5c951
commit
af553c422d
@ -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',
|
||||
|
@ -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):
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user