"""Utilities to deal with uploaded files.""" import hashlib from pathlib import Path from typing import Iterator from datetime import datetime from flask import current_app from werkzeug.utils import secure_filename from werkzeug.datastructures import FileStorage def save_file(fileobj: FileStorage, upload_dir: Path) -> Path: """Save the uploaded file and return the path.""" assert bool(fileobj), "Invalid file object!" hashed_name = hashlib.sha512( f"{fileobj.filename}::{datetime.now().isoformat()}".encode("utf8") ).hexdigest() filename = Path(secure_filename(hashed_name)) # type: ignore[arg-type] if not upload_dir.exists(): upload_dir.mkdir() filepath = Path(upload_dir, filename) fileobj.save(filepath) return filepath def fullpath(filename: str): """Get a file's full path. This makes use of `flask.current_app`.""" return Path(current_app.config["UPLOAD_FOLDER"], filename).absolute() def chunked_binary_read(filepath: Path, chunksize: int = 2048) -> Iterator: """Read a file in binary mode in chunks.""" with open(filepath, "rb") as inputfile: while True: data = inputfile.read(chunksize) if data != b"": yield data continue break def sha256_digest_over_file(filepath: Path) -> str: """Compute the sha256 digest over a file's contents.""" filehash = hashlib.sha256() for chunk in chunked_binary_read(filepath): filehash.update(chunk) return filehash.hexdigest()