[controller] Add controller for handling uploading images
This commit is contained in:
parent
42e304cf5f
commit
a6fe921920
|
@ -0,0 +1,65 @@
|
|||
from datetime import date
|
||||
from flask import send_file
|
||||
from pathlib import Path
|
||||
from PIL import Image as PImage
|
||||
|
||||
from werkzeug.exceptions import NotFound, UnprocessableEntity
|
||||
from werkzeug.datastructures import FileStorage
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from flaschengeist.models.image import Image
|
||||
from flaschengeist.database import db
|
||||
from flaschengeist.config import config
|
||||
|
||||
|
||||
def check_mimetype(mime: str):
|
||||
return mime in config["FILES"].get('allowed_mimetypes', [])
|
||||
|
||||
|
||||
def send_image(id: int = None, image: Image = None):
|
||||
if image is None:
|
||||
image = Image.query.get(id)
|
||||
if not image:
|
||||
raise NotFound
|
||||
return send_file(image.path_, mimetype=image.mimetype_, download_name=image.filename_)
|
||||
|
||||
|
||||
def send_thumbnail(id: int = None, image: Image = None):
|
||||
if image is None:
|
||||
image = Image.query.get(id)
|
||||
if not image:
|
||||
raise NotFound
|
||||
if not image.thumbnail_:
|
||||
with PImage.open(image.open()) as im:
|
||||
im.thumbnail(tuple(config["FILES"].get("thumbnail_size")))
|
||||
s = image.path_.split('.')
|
||||
s.insert(len(s)-1, 'thumbnail')
|
||||
im.save('.'.join(s))
|
||||
image.thumbnail_ = '.'.join(s)
|
||||
db.session.commit()
|
||||
return send_file(image.thumbnail_, mimetype=image.mimetype_, download_name=image.filename_)
|
||||
|
||||
|
||||
def upload_image(file: FileStorage):
|
||||
if not check_mimetype(file.mimetype):
|
||||
raise UnprocessableEntity
|
||||
|
||||
path = Path(config["FILES"].get("data_path")) / str(date.today().year)
|
||||
path.mkdir(mode=int('0700', 8), parents=True, exist_ok=True)
|
||||
|
||||
if file.filename.count('.') < 1:
|
||||
name = secure_filename(file.filename + '.' + file.mimetype.split('/')[-1])
|
||||
else:
|
||||
name = secure_filename(file.filename)
|
||||
img = Image(mimetype_=file.mimetype, filename_=name)
|
||||
db.session.add(img)
|
||||
db.session.flush()
|
||||
try:
|
||||
img.path_ = str((path / f"{img.id}.{img.filename_.split('.')[-1]}").resolve())
|
||||
file.save(img.path_)
|
||||
except:
|
||||
db.session.delete(img)
|
||||
raise
|
||||
finally:
|
||||
db.session.commit()
|
||||
return img
|
|
@ -7,7 +7,7 @@ auth = "auth_plain"
|
|||
# Enable if you run flaschengeist behind a proxy, e.g. nginx + gunicorn
|
||||
#proxy = false
|
||||
# Set root path, prefixes all routes
|
||||
#root = /api
|
||||
root = "/api"
|
||||
# Set secret key
|
||||
secret_key = "V3ryS3cr3t"
|
||||
# Domain used by frontend
|
||||
|
@ -21,10 +21,19 @@ level = "WARNING"
|
|||
|
||||
[DATABASE]
|
||||
# engine = "mysql" (default)
|
||||
# user = "user"
|
||||
# host = "127.0.0.1"
|
||||
# password = "password"
|
||||
# database = "database"
|
||||
|
||||
[FILES]
|
||||
# Path for file / image uploads
|
||||
data_path = "./data"
|
||||
# Thumbnail size
|
||||
thumbnail_size = [192, 192]
|
||||
# Accepted mimetypes
|
||||
allowed_mimetypes = [
|
||||
"image/avif",
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/webp"
|
||||
]
|
||||
|
||||
[auth_plain]
|
||||
enabled = true
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
from __future__ import annotations # TODO: Remove if python requirement is >= 3.10
|
||||
|
||||
from sqlalchemy import event
|
||||
from pathlib import Path
|
||||
|
||||
from . import ModelSerializeMixin, Serial
|
||||
from ..database import db
|
||||
|
||||
class Image(db.Model, ModelSerializeMixin):
|
||||
__tablename__ = "image"
|
||||
id: int = db.Column("id", Serial, primary_key=True)
|
||||
filename_: str = db.Column(db.String(30), nullable=False)
|
||||
mimetype_: str = db.Column(db.String(30), nullable=False)
|
||||
thumbnail_: str = db.Column(db.String(127))
|
||||
path_: str = db.Column(db.String(127))
|
||||
|
||||
def open(self):
|
||||
return open(self.path_, "rb")
|
||||
|
||||
|
||||
@event.listens_for(Image, 'before_delete')
|
||||
def clear_file(mapper, connection, target: Image):
|
||||
if target.path_:
|
||||
p = Path(target.path_)
|
||||
if p.exists():
|
||||
p.unlink()
|
||||
if target.thumbnail_:
|
||||
p = Path(target.thumbnail_)
|
||||
if p.exists():
|
||||
p.unlink()
|
4
setup.py
4
setup.py
|
@ -38,13 +38,13 @@ setup(
|
|||
"sqlalchemy>=1.4",
|
||||
"flask_sqlalchemy>=2.5",
|
||||
"flask_cors",
|
||||
"Pillow>=8.4.0",
|
||||
"werkzeug",
|
||||
mysql_driver,
|
||||
],
|
||||
extras_require={
|
||||
"ldap": ["flask_ldapconn", "ldap3"],
|
||||
"argon": ["argon2-cffi"],
|
||||
"pricelist": ["pillow"],
|
||||
"test": ["pytest", "coverage"],
|
||||
},
|
||||
entry_points={
|
||||
|
@ -59,7 +59,7 @@ setup(
|
|||
"balance = flaschengeist.plugins.balance:BalancePlugin",
|
||||
"events = flaschengeist.plugins.events:EventPlugin",
|
||||
"mail = flaschengeist.plugins.message_mail:MailMessagePlugin",
|
||||
"pricelist = flaschengeist.plugins.pricelist:PriceListPlugin [pricelist]",
|
||||
"pricelist = flaschengeist.plugins.pricelist:PriceListPlugin",
|
||||
],
|
||||
},
|
||||
cmdclass={
|
||||
|
|
Loading…
Reference in New Issue