From c7777ac1d955ca4f26984b25a9589485b1a68fc3 Mon Sep 17 00:00:00 2001 From: AlbertCJC Date: Sun, 19 Oct 2025 19:21:41 +0800 Subject: [PATCH] feat: implemented file-upload --- flask_file_upload_mvc/.gitignore | 3 + .../__pycache__/app.cpython-313.pyc | Bin 0 -> 1375 bytes .../__pycache__/config.cpython-313.pyc | Bin 0 -> 1057 bytes .../__pycache__/db_instance.cpython-313.pyc | Bin 0 -> 263 bytes .../__pycache__/extensions.cpython-313.pyc | Bin 0 -> 262 bytes flask_file_upload_mvc/app.py | 29 ++++++ flask_file_upload_mvc/config.py | 14 +++ .../file_controller.cpython-313.pyc | Bin 0 -> 4036 bytes .../controllers/file_controller.py | 68 +++++++++++++ flask_file_upload_mvc/extensions.py | 3 + flask_file_upload_mvc/logs/app.log | 89 ++++++++++++++++++ .../__pycache__/uploaded_file.cpython-313.pyc | Bin 0 -> 843 bytes flask_file_upload_mvc/models/uploaded_file.py | 6 ++ flask_file_upload_mvc/requirements.txt | Bin 0 -> 804 bytes .../__pycache__/file_service.cpython-313.pyc | Bin 0 -> 4006 bytes .../services/file_service.py | 65 +++++++++++++ 16 files changed, 277 insertions(+) create mode 100644 flask_file_upload_mvc/.gitignore create mode 100644 flask_file_upload_mvc/__pycache__/app.cpython-313.pyc create mode 100644 flask_file_upload_mvc/__pycache__/config.cpython-313.pyc create mode 100644 flask_file_upload_mvc/__pycache__/db_instance.cpython-313.pyc create mode 100644 flask_file_upload_mvc/__pycache__/extensions.cpython-313.pyc create mode 100644 flask_file_upload_mvc/app.py create mode 100644 flask_file_upload_mvc/config.py create mode 100644 flask_file_upload_mvc/controllers/__pycache__/file_controller.cpython-313.pyc create mode 100644 flask_file_upload_mvc/controllers/file_controller.py create mode 100644 flask_file_upload_mvc/extensions.py create mode 100644 flask_file_upload_mvc/logs/app.log create mode 100644 flask_file_upload_mvc/models/__pycache__/uploaded_file.cpython-313.pyc create mode 100644 flask_file_upload_mvc/models/uploaded_file.py create mode 100644 flask_file_upload_mvc/requirements.txt create mode 100644 flask_file_upload_mvc/services/__pycache__/file_service.cpython-313.pyc create mode 100644 flask_file_upload_mvc/services/file_service.py diff --git a/flask_file_upload_mvc/.gitignore b/flask_file_upload_mvc/.gitignore new file mode 100644 index 0000000..5e19434 --- /dev/null +++ b/flask_file_upload_mvc/.gitignore @@ -0,0 +1,3 @@ +.env + +/uploads/ \ No newline at end of file diff --git a/flask_file_upload_mvc/__pycache__/app.cpython-313.pyc b/flask_file_upload_mvc/__pycache__/app.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4929f4dc0e6658ebb11c9b712cd064a0aef84d67 GIT binary patch literal 1375 zcmaJ>O>Y}j6ut9p&)6Z5kPuU~Zd$8$NsH7rLa87UN=T@k#$rrWBnFMf_Sl(Z>@jb~ zQ5&&{DUy>_vOy{;fi}fzdVd1q+gBDaQ)vdv03zzJ=kY@fX@Gy;w5zs{X~t- z`TPJ!9`F#ltQE%c<77N!zjKjojGuEv({Ej5M0%*kv0pSBD#l8Q`=0?=qc~|?fA`{E z^eZ{XrJ>H%xvMY(*)vKN&$@;_UcnjDu$uWmxB@jUH}VIoujV)^ExDGzfj}*Nvhtx@ zS+g5YF_IU&X3eQnb)~p$E)`c6Khi(Nbj*I@_<`wdY$;Z&b=&poTQ@RU%dZ4Z!_N8X z&of!q-n8AO#lkPrnQX)MeXEW^PN1=xn%#CIY?y9SyPRf9YC-(TT{?awVLeu$#mbSx?iX0?tge z8u1#YS6;U(0p+V@8grUXV6sxIVQtt|hxk-0TfS2{^FVA_!5S6UJ*P><2dhg(sv+CN z9)bNNpl>rkJlC~}e>;4QVS;FEf;Wg=N84;-mfg19B2F`)YK7QVV4IffQh~IaKfx(d zffg6;8;^XHGS;oiox8@O?QaBL%UD8x9<^N0s)h$<7eKs&;y~XcGP2- zIDm=$%_F#sMS1j@vCa2@?`j=wXWYsbMWfyCed6nF#q%W|M?$G+vVZ_JG`;|;1B}v zOH+Es)D_x3ChP$O5DWvNaYGoc8;D^BNCppB?!sok1>gb5=myBNZ0Hz?u>g7Vsiwqu zNF60PvxzrlHCNfmcEfb$>?&;=D`F2wfItL@0YPF!2FaAvi|h{o`cumct(_tE;rRg|4~aEu^))_!Xo%R+FyYV$aTG@RF>3M4m3*;q zYGF-28Y$P6?b2Meu5;h~Jhh4|IE?~H3VqbU|LdX=8TSWz=-F^e;*N{aVuYEHqJj4^ndT|kq5 zpOjW|M3Ek5vWrWSm{Yb?xhe&VrCeHXF<8ndTT-6sS2{gQdCkJQVBK{rxV)2Yw oyVcp?Z8FPL?c*qJM%c3uegpRJVCpAuJazps+2H7Jz=$6E7iB5@~ literal 0 HcmV?d00001 diff --git a/flask_file_upload_mvc/__pycache__/db_instance.cpython-313.pyc b/flask_file_upload_mvc/__pycache__/db_instance.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f0b124c674396fd0e5c2a434f5575857286fda5 GIT binary patch literal 263 zcmey&%ge<81S?oRX9NK0#~=<2FhLog<$#Q-48aV+jNS}hj75wJ3^9yK45m=&BBpdE zP3D&%o+jfhuHZl)$DHJh)Z9uxP3BtyX*r3-+404NIf)R#B4(hvTTCfQD;Yk6wA>1J zwu%WYPAw{q$x2K%GLCUcEzT~QDM|5}dBr7(dC94I1(mlrY;yBcN^?@}ia3GxFamKgKalvq%*e?2ltKL=D}QrL KBU2GOPz(U9*GHfL literal 0 HcmV?d00001 diff --git a/flask_file_upload_mvc/__pycache__/extensions.cpython-313.pyc b/flask_file_upload_mvc/__pycache__/extensions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f02ddcf19c52b41c7a199fe415748f685ba49640 GIT binary patch literal 262 zcmey&%ge<81b>fw$*=*^k3k$5V1hC}%K;fv8G;#t8NC_27>gJc7-ATe7)+tkMNH{T zn#?ajJWa-1T)}}pjycI0skxPYn#{KZ(sB}uv*U{ka}ptfMa)2Tx0q6rRx*4BX}J~V zY!wq)oLW>Ila-ikWE|s?TAW>yUl8M#nUfk?kdvR7;s!DzCJo5fEd>k2Knw+m#6v{l zbIX!rQY%VQ^NKU`^NRHfDsOSvjmw2iMLz!Sl(?d-g{o^?X|ppE#Cf@dP|mf zdy97<$A>$f$UJ zQ}g+P66W9`eOECabWgRMO;_xPtWr>{hcKhJCCY_TI;&)_z_3}}iKA2BDla<6s>TbQ zzj0l@369R&N@byxRWr?VB*Y019Q?NbY+Pn9TxPQIK5kt@qXQb}z%MNklC)s}S1fbm z%xUPyBJ-!%0rKiOWCjNpNz!2q0TUPPRP29M4J#sm9<@&YD(YVfxEq0iI3aQmh2FA`; z3t^7p`RQyvtMlb_W#Rds;BnDsIVzNv=wv7?QM@o+z*SU=cT0KfaAZow`Fu|2t5ujl zKzmB(7LC8FxJpHwL-%@Z8v6}FBP&1 z)kXVzbVgBey$}>VcWH7ip(<3JyOqwII5~G&QQxYR%5zs>Z|1hI6w>NjbGYn?Im6sM z_85dqnLVl7cQbRa`3fx+3OJXs$!5oB`94w;>1Pb1gRneeRowvdfYfn^^%y-s*n`LK{HhyBw*@?qpG*tMYh zQa`tLWc*T)`=pzJ_9sCB?4o}Rk85lS;@+fjaQWW@NU7KAaJxK-tWaVB(DsWA|YwlZ6SB+>Nkf zk(*66LQNde_(B14I}+>*^?~G2w~$+$HTbyby8;Is zDwZmt`BJr*&Ho6F5F@=et)|kIN;N2lgWe;c*Hr>?}9-lhyXU} zz=qUUllnHK!!_yfN^V^mUE=>L`7K(U)egM2?w@#aQuCkJ+RlG2?OhIh(EDERFDf-@ zN)xBP-1G!?pw_bzSvje79eFAq{YupK!#*CKig?Jc{a{u-?&)@JwR3#h!>tJnwAVZW zSe?6(cnLm>7C!%v0?9P-`zDiYNaU_qHN^5X-UgH%(gsM}O*UocXajq)Xo*+@)eRAA zpnErY1f08GDPkDgjqYIfHFlUE$%ajU?xe~7U3$~53>eU zD5~@GKcX+F7UohmA;KO*wJ3_UNdk-thH%k+*m@})czO)q8)D?N);2j|tB^^GfR(%{ z_ws55#LOhf2^c#JKlPVjkRaZ#Ar*Uq%ekK?H%2bhMlO6T)kbct@A<}((~yG`&jJTF z0*7mX!&?G}I>3PrI8Xf@CdQ6Jc4mS!}Egvi1(#Nu7zHzZwn(nhiH%Dg(I1ibPI=7qT2vPKfa zqEQl!gtjFv3yrwUL>MD3L){XW9ada6;b^AmJK;#jVfIDeWg~;PA!x>A2So`oA-G%g zWlgvZfRl=OxgB!@*584h@WTH@ucK=k48lu5vG+f^`c&*OVx@NIyw-hTUAp-3Ax(;E zV)Q?v>wl9IV1oN_Uk`cT9qHpf8jtjFt6dB5E2hZ58N)n*Q%pt&ZSsc7hW309TXvGeOpPTD5|zD&{DOc z7^xXWRiel|is$n)vJIA4(lrHhR}0E{ipPd%RDT7g&M^%0Hxm7nM8Vcb^e<%OGvfJ- z`2OzpKYC;N-n#qf11AuRb$u`Ro#4{_l`|_x9|SdKc#Cakj&E~>Jyyq7eb92zq=CoP u$2s&lv&FhhpR;vr)%Wh|<7@aptKQ$@bMNsjoM&Q-b(lVrMvG3t&i@S#b1oPF literal 0 HcmV?d00001 diff --git a/flask_file_upload_mvc/controllers/file_controller.py b/flask_file_upload_mvc/controllers/file_controller.py new file mode 100644 index 0000000..30593f9 --- /dev/null +++ b/flask_file_upload_mvc/controllers/file_controller.py @@ -0,0 +1,68 @@ +from flask import Blueprint, current_app, request, jsonify, send_file +from services.file_service import save_file, update_file, delete_file +from models.uploaded_file import UploadedFile +import uuid +import logging + +file_bp = Blueprint('file_bp', __name__) + +# POST - Upload file +@file_bp.route('/upload', methods=['POST']) +def upload_file(): + try: + if 'file' not in request.files: + return jsonify({'message': 'No file uploaded'}), 400 + + file = request.files['file'] + folder = f"{current_app.config['UPLOAD_FOLDER']}/{uuid.uuid4().hex}" # new folder per request + uploaded = save_file(file, folder) + + return jsonify({ + 'message': 'Upload successful', + 'id': uploaded.id, + 'path': uploaded.file_path + }), 201 + except Exception as e: + logging.error(f"Upload error: {e}") + return jsonify({'message': str(e)}), 400 + +# GET - Retrieve file info or download +@file_bp.route('/file/', methods=['GET']) +def get_file(file_id): + try: + file = UploadedFile.query.get(file_id) + if not file: + return jsonify({'message': 'File not found'}), 404 + + return send_file(file.file_path, as_attachment=True) + except Exception as e: + logging.error(f"Get error: {e}") + return jsonify({'message': str(e)}), 400 + +# PUT - Update file +@file_bp.route('/file/', methods=['PUT']) +def update_existing_file(file_id): + try: + if 'file' not in request.files: + return jsonify({'message': 'No file provided'}), 400 + + file = request.files['file'] + updated = update_file(file_id, file, current_app.config['UPLOAD_FOLDER']) + + return jsonify({ + 'message': 'File updated successfully', + 'path': updated.file_path + }), 200 + except Exception as e: + logging.error(f"Update error: {e}") + return jsonify({'message': str(e)}), 400 + +# DELETE - Delete file +@file_bp.route('/file/', methods=['DELETE']) +def delete_existing_file(file_id): + try: + delete_file(file_id) + return jsonify({'message': 'File deleted successfully'}), 200 + except Exception as e: + logging.error(f"Delete error: {e}") + return jsonify({'message': str(e)}), 400 diff --git a/flask_file_upload_mvc/extensions.py b/flask_file_upload_mvc/extensions.py new file mode 100644 index 0000000..2e1eeb6 --- /dev/null +++ b/flask_file_upload_mvc/extensions.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() \ No newline at end of file diff --git a/flask_file_upload_mvc/logs/app.log b/flask_file_upload_mvc/logs/app.log new file mode 100644 index 0000000..b0b9b2a --- /dev/null +++ b/flask_file_upload_mvc/logs/app.log @@ -0,0 +1,89 @@ +2025-10-19 19:02:36,047 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 19:02:36,047 [INFO] Press CTRL+C to quit +2025-10-19 19:02:36,048 [INFO] * Restarting with stat +2025-10-19 19:02:36,529 [WARNING] * Debugger is active! +2025-10-19 19:02:36,531 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:04:04,348 [INFO] 127.0.0.1 - - [19/Oct/2025 19:04:04] "GET /upload HTTP/1.1" 405 - +2025-10-19 19:05:01,747 [ERROR] Upload error: module 'flask.app' has no attribute 'config' +2025-10-19 19:05:01,748 [INFO] 127.0.0.1 - - [19/Oct/2025 19:05:01] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:06:16,772 [INFO] * Detected change in 'C:\\Users\\jac23\\Desktop\\FileUploadFlask\\file-upload\\flask_file_upload_mvc\\controllers\\file_controller.py', reloading +2025-10-19 19:06:16,860 [INFO] * Restarting with stat +2025-10-19 19:06:17,438 [WARNING] * Debugger is active! +2025-10-19 19:06:17,440 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:06:35,549 [INFO] * Detected change in 'C:\\Users\\jac23\\Desktop\\FileUploadFlask\\file-upload\\flask_file_upload_mvc\\controllers\\file_controller.py', reloading +2025-10-19 19:06:35,646 [INFO] * Restarting with stat +2025-10-19 19:06:36,148 [WARNING] * Debugger is active! +2025-10-19 19:06:36,150 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:06:49,020 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 19:06:49,020 [INFO] Press CTRL+C to quit +2025-10-19 19:06:49,021 [INFO] * Restarting with stat +2025-10-19 19:06:49,493 [WARNING] * Debugger is active! +2025-10-19 19:06:49,495 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:06:52,723 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 19:06:52,723 [INFO] 127.0.0.1 - - [19/Oct/2025 19:06:52] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:06:53,605 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 19:06:53,605 [INFO] 127.0.0.1 - - [19/Oct/2025 19:06:53] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:07:21,219 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 19:07:21,219 [INFO] 127.0.0.1 - - [19/Oct/2025 19:07:21] "POST /upload?file= HTTP/1.1" 400 - +2025-10-19 19:08:09,972 [INFO] * Detected change in 'C:\\Users\\jac23\\Desktop\\FileUploadFlask\\file-upload\\flask_file_upload_mvc\\models\\uploaded_file.py', reloading +2025-10-19 19:08:10,064 [INFO] * Restarting with stat +2025-10-19 19:09:10,245 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 19:09:10,245 [INFO] Press CTRL+C to quit +2025-10-19 19:09:10,246 [INFO] * Restarting with stat +2025-10-19 19:09:10,722 [WARNING] * Debugger is active! +2025-10-19 19:09:10,723 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:09:28,337 [INFO] 127.0.0.1 - - [19/Oct/2025 19:09:28] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:09:35,428 [INFO] 127.0.0.1 - - [19/Oct/2025 19:09:35] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:11:12,434 [INFO] * Detected change in 'C:\\Users\\jac23\\Desktop\\FileUploadFlask\\file-upload\\flask_file_upload_mvc\\controllers\\file_controller.py', reloading +2025-10-19 19:11:12,529 [INFO] * Restarting with stat +2025-10-19 19:11:13,076 [WARNING] * Debugger is active! +2025-10-19 19:11:13,078 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:11:23,462 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 19:11:23,463 [INFO] Press CTRL+C to quit +2025-10-19 19:11:23,464 [INFO] * Restarting with stat +2025-10-19 19:11:23,998 [WARNING] * Debugger is active! +2025-10-19 19:11:24,000 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:11:37,529 [INFO] 127.0.0.1 - - [19/Oct/2025 19:11:37] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:11:54,450 [INFO] 127.0.0.1 - - [19/Oct/2025 19:11:54] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:11:55,229 [INFO] 127.0.0.1 - - [19/Oct/2025 19:11:55] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:11:55,687 [INFO] 127.0.0.1 - - [19/Oct/2025 19:11:55] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:11:56,070 [INFO] 127.0.0.1 - - [19/Oct/2025 19:11:56] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:11:56,387 [INFO] 127.0.0.1 - - [19/Oct/2025 19:11:56] "POST /upload HTTP/1.1" 400 - +2025-10-19 19:14:45,256 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 19:14:45,256 [INFO] 127.0.0.1 - - [19/Oct/2025 19:14:45] "POST /upload?file= HTTP/1.1" 400 - +2025-10-19 19:15:28,149 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 19:15:28,149 [INFO] Press CTRL+C to quit +2025-10-19 19:15:28,150 [INFO] * Restarting with stat +2025-10-19 19:15:28,681 [WARNING] * Debugger is active! +2025-10-19 19:15:28,683 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:15:44,421 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 19:15:44,422 [INFO] 127.0.0.1 - - [19/Oct/2025 19:15:44] "POST /upload?file HTTP/1.1" 400 - +2025-10-19 19:17:26,379 [INFO] * Detected change in 'C:\\Users\\jac23\\Desktop\\FileUploadFlask\\file-upload\\flask_file_upload_mvc\\app.py', reloading +2025-10-19 19:17:26,464 [INFO] * Restarting with stat +2025-10-19 19:17:27,076 [WARNING] * Debugger is active! +2025-10-19 19:17:27,078 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:17:34,140 [INFO] * Detected change in 'C:\\Users\\jac23\\Desktop\\FileUploadFlask\\file-upload\\flask_file_upload_mvc\\app.py', reloading +2025-10-19 19:17:34,236 [INFO] * Restarting with stat +2025-10-19 19:17:38,566 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 19:17:38,566 [INFO] Press CTRL+C to quit +2025-10-19 19:17:38,567 [INFO] * Restarting with stat +2025-10-19 19:17:39,047 [WARNING] * Debugger is active! +2025-10-19 19:17:39,049 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:17:54,080 [ERROR] Upload error: The current Flask app is not registered with this 'SQLAlchemy' instance. Did you forget to call 'init_app', or did you create multiple 'SQLAlchemy' instances? +2025-10-19 19:17:54,081 [INFO] 127.0.0.1 - - [19/Oct/2025 19:17:54] "POST /upload?file HTTP/1.1" 400 - +2025-10-19 19:18:59,550 [INFO] * Detected change in 'C:\\Users\\jac23\\Desktop\\FileUploadFlask\\file-upload\\flask_file_upload_mvc\\app.py', reloading +2025-10-19 19:18:59,666 [INFO] * Restarting with stat +2025-10-19 19:19:07,180 [INFO] WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. + * Running on http://127.0.0.1:5000 +2025-10-19 19:19:07,181 [INFO] Press CTRL+C to quit +2025-10-19 19:19:07,181 [INFO] * Restarting with stat +2025-10-19 19:19:07,770 [WARNING] * Debugger is active! +2025-10-19 19:19:07,772 [INFO] * Debugger PIN: 134-261-731 +2025-10-19 19:19:26,790 [INFO] File saved: uploads/c543263cbc4e4783944f6fad9d2b2596\01f55afa347545b4b564e5be8d596494_Screenshot_2025-10-03_223338.png +2025-10-19 19:19:26,794 [INFO] 127.0.0.1 - - [19/Oct/2025 19:19:26] "POST /upload?file HTTP/1.1" 201 - diff --git a/flask_file_upload_mvc/models/__pycache__/uploaded_file.cpython-313.pyc b/flask_file_upload_mvc/models/__pycache__/uploaded_file.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77dd0cbcac9e20a8e1c9812616a222d839d7b42f GIT binary patch literal 843 zcmbtSzi$&U6t;7hAD5&`s01_}5bAaV9a2>)p<*BqQ4k&KW;@ATa!z{Wau@77M7Byu z3|&f*s#fjLj<9v(KVU@47Fl9qK-rK|W_Zpu6okZrCEMTo{C<5;9?#5V5v&g%&$qt9 z{Y4qmmAnAs3V?k?5W$bpBh2wlG=~-tNz5anS$IRA23+3DT8z2Y*3INd877tqr_sFh zLFiY3Pr&XY3vo;kPY}#COW&9@_PaZRqQK|>3>8O|BeC)`ZA7rk|Zg} zVElK6EJ^cRJ7XF$U+;N=?J%e2dTgWyvI#TD^|;%$d7pMzfAj;$8ju-3@;tlYvG-`* z%*za=zTIV%$}FYbz==GC^OQc1Z13WwKxxb6BJ^CJ`2nR|6_BZF;6+_urq}$CwHcQQ z*O4g_a@TLmObZ%O;nGlu_Uv#=UZqrocIY;#9fDgU3YmaxYWQQ=NItDrth!)aSkLTc zdC9uZL?;Y-mQocfta-NRSSsbMXd>BaDF*t3A~8d|FPau8VV#a^GQZ!kHO z1W|+IH5wYZ-O^6!P5Gd*S9!NNFv{EahJ`D;{hj{d>`CdUbh>nW6-S&gTD hm(PQ#zKEEEThN&1(eQS5c4oHU zUoqa1;sX;bkl>O}4<|eW{+{6&M~JaxtwfF?Z$s7`6%wJ-Gt`7U3nDYJty-Q7!-yrZ zdcwN8hrB=CtL3cb?2D-+HiVe_JOg9DVLhe>?^dT&Mpm1(wSZH^QW}FCbox4m03#}$ z+q8;mrAL#*82*|T;1Smt;|Vj=oFl9_UBO@U7!SC^6_E*EE!U0pG>Sbd%})6&iAzzn zh=|O6Q?TG|iK%5jCuT)o_0E@2IzIi@Qhy^dYI)?ua~ttKW3O-QP`7F;R%;Y=7}KF= z;d>EQd%MSmd2NW0vxcWjzhJGR*PLzwD$e;&&OQ&Y?%T0jt3=cv@e6CVSklLOujjmV w_+O@;ff9426_pg0#vCuyu-+*Vv())l;hWZ6&+pmN&9LRW=2szy&9OFeHSJrW?|<*{C53ICUgDUQmtXx}L!}7>|3$ z4un-((-(I860)lb@)S$^lD_3>|APGkJhfLw6H-;Xy!34q`mpL#&$;8V9f#8H?vZ@v zex7^pnR|Y7eAv;^PCy?1{fFgE8zKL~iXo<2;qW6Ud_}?}%moP5pB&|&ZVH&@c*^^* zjSmQOW@=_-0m>F?VP!60rPc`&Hm`8h7Ph=WXj|9{x;<A148h|@0QnWGk$u!VUrrnkga z?yv;&@NaOuZ1!WszCPa&h6npU8~yXch{eM{`m8! z%CqZb@7vElQN7bu?={tXt?a#CnVDC;^Ce;a5ay7Cad)tSE@R$rht{u1wC-c@ck?(L z3v#K}Z@e4PPDm7OFbQ);zr;l?Mr#P}ZmaZ@2)wy4KW*}nDA>^D_%$=8sKXc;avG(^ zv!596Idr1YYLuKLEJRFBGR0^3roFk1{>C%pX_Sr9LYHyNG6L+f*Jy7%BZ6^uIMGR? zec`RJdD@gQN8d17LT?(S#xvw?l#Nov++^?g*=LM7ji=!W*h|0B8X7Q4jb}dzTOtB@ z#nf0WVur}D1~)u-hkdyRS;?1?bH3Uwy!$l-fwZ1daxr;D<6?dj?F4&T8{Y6(HlADF zaIlF={Jtb!@%g*hmDLBe|3041OEZ+pR5QtnW?PM~NMedA6z`xWHg(n@4JF4~})jqWE>?@rdFFPknwuuA5vK9Cuux&4j-w31ouG71@C!aq4wCuW2 zxV7&&x%1(lKm5yL)ibDi220&T#rRWOaiip(EClvXxOc3NtUHcJj@@Ya#M_10J;Am$ zyE*&ER7L1JaGfefRo6{00t?J-&TR!ZgWH!Y!ke{vRq&{Sry}$o2==YpU) zL#cDLBAnl|cNOl?ZrFOFnU-}H?CC6keqD|tL8u<}#wY@eHf~XdkMOTB!fK3qg%Nc! zgxHtP7*ksn9o!-j{;0?|Ry9_P`+!Of+egp<_;ai!h?~azqxOavZjs|HW`rh-==v6c z9SLm$=eI#M7@^VMH`DB3hdEcGPK)Ak4zeIuWg~%d@ZBk64X; z`p7~eU1#@#lCIx|Xq`!+q1P>h{I9n)k!{524V^bijVF-J93d@aQHLN8%5{PQBVqAWF2EZpAIs+em*^+xD4I66Xidk0A;RIRo5BSb*AbX zR9%C`WZ5-Vxb>~Qi$Tp_c8-*6BL~*Dt+maytev7Ogdz8ZL;TOd)(|vdZ)Iu0fEfT3Gl(2{6XJc5kNE0f4Y5#mHDw4!61nEVvxN(Xh zL6p)GAi=jpft0&+gNDELPpas(bipZ`nOj z2z+brX^!Xprg&bd_Kd1MqgCs9)q38D=h3SBoa#PTu?_FrTn`;zI!e8h&)%s{-BPD+ zl_qCPp1F!GfT<2-B6{Yry;2eSTjOtE&qYdIL&ddXs?<6D4`G7kA^r{>r}!2%C;a_d z*^u(dv3xF-uH}Z^t1_HY8PoqU8UKbbN_DR>C*qf0Pd70^qlh+&{`i`(Rdo8xCQQ|#l>~M9Pn`6 LhvV(sB(wPw#bLZn literal 0 HcmV?d00001 diff --git a/flask_file_upload_mvc/services/file_service.py b/flask_file_upload_mvc/services/file_service.py new file mode 100644 index 0000000..320f365 --- /dev/null +++ b/flask_file_upload_mvc/services/file_service.py @@ -0,0 +1,65 @@ +import os +import uuid +from werkzeug.utils import secure_filename +from models.uploaded_file import UploadedFile +from models.uploaded_file import db +import logging + +ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'pdf', 'txt'} + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +def save_file(file, base_folder): + if not allowed_file(file.filename): + raise ValueError('File type not allowed') + + os.makedirs(base_folder, exist_ok=True) + filename = secure_filename(file.filename) + unique_name = f"{uuid.uuid4().hex}_{filename}" + save_path = os.path.join(base_folder, unique_name) + file.save(save_path) + + new_file = UploadedFile(filename=unique_name, file_path=save_path) + db.session.add(new_file) + db.session.commit() + + logging.info(f"File saved: {save_path}") + return new_file + +# Update service reuse the existing folder and remove the bug where after updating it gets added again in the database +def update_file(file_id, new_file, _): + existing = UploadedFile.query.get(file_id) + if not existing: + raise ValueError('File not found') + + if os.path.exists(existing.file_path): + os.remove(existing.file_path) + + existing_folder = os.path.dirname(existing.file_path) + os.makedirs(existing_folder, exist_ok=True) + filename = secure_filename(new_file.filename) + unique_name = f"{uuid.uuid4().hex}_{filename}" + save_path = os.path.join(existing_folder, unique_name) + + new_file.save(save_path) + + existing.filename = unique_name + existing.file_path = save_path + db.session.commit() + + logging.info(f"File updated: ID={file_id}") + return existing + + +def delete_file(file_id): + file_record = UploadedFile.query.get(file_id) + if not file_record: + raise ValueError('File not found') + if os.path.exists(file_record.file_path): + os.remove(file_record.file_path) + logging.info(f"File deleted from storage: {file_record.file_path}") + + db.session.delete(file_record) + db.session.commit() + logging.info(f"Record deleted: ID={file_id}") \ No newline at end of file