From 203e37ea125e251d39c3dbb5337c124d6eac7b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aristide=20Manyess=C3=A9?= Date: Wed, 16 Jul 2025 10:48:00 +0000 Subject: [PATCH] Ajout des fichiers de configuration et du code pour le redimensionnement d'images avec Bottle et Pillow --- .python-version | 1 + Pipfile | 13 +++++ Pipfile.lock | 142 +++++++++++++++++++++++++++++++++++++++++++++++ app.py | 99 +++++++++++++++++++++++++++++++++ dockerfile | 24 ++++++++ requirements.txt | 3 + views/index.tpl | 116 ++++++++++++++++++++++++++++++++++++++ views/result.tpl | 96 ++++++++++++++++++++++++++++++++ 8 files changed, 494 insertions(+) create mode 100644 .python-version create mode 100644 Pipfile create mode 100644 Pipfile.lock create mode 100644 app.py create mode 100644 dockerfile create mode 100644 requirements.txt create mode 100644 views/index.tpl create mode 100644 views/result.tpl diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..c8cfe39 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10 diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..362b2b5 --- /dev/null +++ b/Pipfile @@ -0,0 +1,13 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +bottle = "*" +pillow = "*" + +[dev-packages] + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..906853c --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,142 @@ +{ + "_meta": { + "hash": { + "sha256": "32de86725c93602d28c892b89df1895b4d6e587b1c4dc93365217e5146a0812a" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "bottle": { + "hashes": [ + "sha256:045684fbd2764eac9cdeb824861d1551d113e8b683d8d26e296898d3dd99a12e", + "sha256:787e78327e12b227938de02248333d788cfe45987edca735f8f88e03472c3f47" + ], + "index": "pypi", + "version": "==0.13.4" + }, + "pillow": { + "hashes": [ + "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", + "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", + "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", + "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", + "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", + "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", + "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06", + "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", + "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", + "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", + "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", + "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", + "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", + "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", + "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", + "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", + "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", + "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", + "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", + "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", + "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", + "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4", + "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", + "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", + "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", + "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", + "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", + "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", + "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", + "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", + "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", + "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", + "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", + "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", + "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", + "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", + "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f", + "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", + "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", + "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", + "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", + "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", + "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", + "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", + "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc", + "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", + "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", + "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", + "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", + "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978", + "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb", + "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", + "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", + "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", + "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", + "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", + "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", + "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081", + "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", + "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", + "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", + "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", + "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", + "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", + "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", + "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", + "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", + "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", + "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", + "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", + "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", + "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", + "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", + "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d", + "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", + "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", + "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", + "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", + "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", + "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", + "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", + "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", + "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", + "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", + "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", + "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", + "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", + "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", + "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", + "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a", + "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", + "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", + "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", + "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", + "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada", + "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", + "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71", + "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", + "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", + "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", + "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", + "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", + "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", + "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", + "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", + "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==11.3.0" + } + }, + "develop": {} +} diff --git a/app.py b/app.py new file mode 100644 index 0000000..4932a4e --- /dev/null +++ b/app.py @@ -0,0 +1,99 @@ +from bottle import Bottle, response, request, run, static_file, template, BaseRequest +from PIL import Image +import os +import tempfile +import zipfile +from concurrent.futures import ProcessPoolExecutor +from functools import partial + +BaseRequest.MEMFILE_MAX = 100 * 1024 * 1024 # Supporte jusqu'à 100 Mo + +app = Bottle() +UPLOAD_DIR = tempfile.mkdtemp() +OUTPUT_DIR = os.path.join(UPLOAD_DIR, 'resized') +ZIP_PATH = os.path.join(UPLOAD_DIR, 'resized_images.zip') + +os.makedirs(OUTPUT_DIR, exist_ok=True) + +def resize_image(filepath, output_dir, ratio): + from PIL import Image # Re-importé dans chaque processus + import os + + try: + with Image.open(filepath) as img: + exif_data = img.info.get('exif') + width, height = img.size + new_size = (int(width / ratio), int(height / ratio)) + + try: + img = img.resize(new_size, Image.LANCZOS) + except ValueError: + img = Image.open(filepath).point(lambda x: x / 256).convert("L") + img = img.resize(new_size, Image.LANCZOS) + + name, ext = os.path.splitext(os.path.basename(filepath)) + output_path = os.path.join(output_dir, f"{name}_resized{ext}") + img.save(output_path, quality=85, optimize=True, exif=exif_data or []) + print(f"✅ Image enregistrée : {output_path} === ({width, height}) => ({new_size})") # DEBUG + return output_path + except Exception as e: + print(f"❌ Erreur pour {filepath}: {e}") + return None + +@app.route('/') +def index(): + return template('index.tpl') + + +@app.post('/upload') +def upload(): + files = request.files.getall('files') + ratio = float(request.forms.get('ratio', 2)) + + if not files: + return "Aucun fichier reçu." + + saved_paths = [] + for file in files: + filename = os.path.basename(file.filename) + save_path = os.path.join(UPLOAD_DIR, filename) + file.save(save_path, overwrite=True) + saved_paths.append(save_path) + + # 🧠 Traitement en parallèle + resize_func = partial(resize_image, output_dir=OUTPUT_DIR, ratio=ratio) + with ProcessPoolExecutor() as executor: + resized_files = list(executor.map(resize_func, saved_paths)) + + # 📦 Création du ZIP + with zipfile.ZipFile(ZIP_PATH, 'w') as zipf: + for path in resized_files: + if path: # Skip si erreur + zipf.write(path, arcname=os.path.basename(path)) + + return template( + 'result.tpl', + count=len([p for p in resized_files if p]), + ratio=ratio, + images=[os.path.basename(p) for p in resized_files if p] + ) + + +@app.route('/download') +def download(): + if not os.path.exists(ZIP_PATH): + return "ZIP non généré ❌" + return static_file('resized_images.zip', root=UPLOAD_DIR, download='images_reduites.zip') + + +@app.route('/resized/') +def serve_resized(filename): + response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate' + response.headers['Pragma'] = 'no-cache' + response.headers['Expires'] = '0' + return static_file(filename, root=OUTPUT_DIR) + + + +if __name__ == "__main__": + run(app, host='localhost', port=8080, debug=True) diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..06012dd --- /dev/null +++ b/dockerfile @@ -0,0 +1,24 @@ +# ✅ Base image officielle Python +FROM python:3.11-slim + +# 📦 Installe les dépendances système pour Pillow +RUN apt-get update && apt-get install -y \ + libjpeg-dev zlib1g-dev libpng-dev \ + && rm -rf /var/lib/apt/lists/* + +# 📁 Dossier de travail dans le conteneur +WORKDIR /app + +# 📦 Copier les fichiers nécessaires +COPY app.py ./ +COPY views/ ./views/ +COPY requirements.txt ./ + +# 📦 Installer les dépendances Python +RUN pip install --no-cache-dir -r requirements.txt + +# 🌍 Expose le port utilisé par Bottle +EXPOSE 8080 + +# 🚀 Commande de lancement +CMD ["python", "app.py"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e354917 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +-i https://pypi.org/simple +bottle==0.13.4 +pillow==11.3.0; python_version >= '3.9' diff --git a/views/index.tpl b/views/index.tpl new file mode 100644 index 0000000..a95beaf --- /dev/null +++ b/views/index.tpl @@ -0,0 +1,116 @@ + + + + + + Réduction d'images + + + +
+

📷 Réduction d’images

+
+ + + + + + + +
+ +
+

Traitement des images en cours... Patientez ⏳

+
+
+
+ + + + diff --git a/views/result.tpl b/views/result.tpl new file mode 100644 index 0000000..e4093ab --- /dev/null +++ b/views/result.tpl @@ -0,0 +1,96 @@ + + + + + + Téléchargement prêt + + + + + + + +
+

📦 Vos images sont prêtes !

+ +
+

{{ count }} images ont été redimensionnées

+

Ratio appliqué : {{ ratio }}

+
+ + + + ⬇️ Télécharger les images réduites +
+ + + + +