1- from fileinput import filename
2- from pickletools import optimize
3- from termios import IMAXBEL
41import uuid
52from io import BytesIO
6- from pathlib import Path
73from PIL import Image , ImageOps
4+ import boto3
5+ from starlette .concurrency import run_in_threadpool
6+ from config import settings
87
9- PROFILE_PICS_DIR = Path ("media/profile_pics" )
8+ def _get_s3_client ():
9+ return boto3 .client (
10+ "s3" ,
11+ region_name = settings .s3_region ,
12+ aws_access_key_id = (
13+ settings .s3_access_key_id .get_secret_value ()
14+ if settings .s3_access_key_id
15+ else None
16+ ),
17+ aws_secret_access_key = (
18+ settings .s3_secret_access_key .get_secret_value ()
19+ if settings .s3_secret_access_key
20+ else None
21+ ),
22+ endpoint_url = settings .s3_endpoint_url ,
23+ )
1024
11- def process_profile_image (content : bytes ) -> str :
25+ def process_profile_image (content : bytes ) -> tuple [ bytes , str ] :
1226 with Image .open (BytesIO (content )) as original :
1327 img = ImageOps .exif_transpose (original )
1428 img = ImageOps .fit (img , (300 , 300 ), method = Image .Resampling .LANCZOS )
@@ -17,17 +31,36 @@ def process_profile_image(content: bytes) -> str:
1731 img = img .convert ("RGB" )
1832
1933 filename = f"{ uuid .uuid4 ().hex } .jpg"
20- filepath = PROFILE_PICS_DIR / filename
21- PROFILE_PICS_DIR .mkdir (parents = True , exist_ok = True )
22- img .save (filepath , "JPEG" , quality = 85 , optimize = True )
34+
35+ output = BytesIO ()
36+ img .save (output , "JPEG" , quality = 85 , optimize = True )
37+ output .seek (0 )
2338
24- return filename
39+ return output . read (), filename
2540
2641
27- def delete_profile_image (filename : str | None ) -> None :
42+ def _upload_to_s3 (file_bytes : bytes , key : str ) -> None :
43+ s3 = _get_s3_client ()
44+ s3 .upload_fileobj (
45+ BytesIO (file_bytes ),
46+ settings .s3_bucket_name ,
47+ key ,
48+ ExtraArgs = {"ContentType" : "image/jpeg" },
49+ )
50+
51+
52+ def _delete_from_s3 (key : str ) -> None :
53+ s3 = _get_s3_client ()
54+ s3 .delete_object (Bucket = settings .s3_bucket_name , Key = key )
55+
56+
57+ async def upload_profile_image (file_bytes : bytes , filename : str ) -> None :
58+ key = f"profile_pics/{ filename } "
59+ await run_in_threadpool (_upload_to_s3 , file_bytes , key )
60+
61+
62+ async def delete_profile_image (filename : str | None ) -> None :
2863 if filename is None :
2964 return
30-
31- filepath = PROFILE_PICS_DIR / filename
32- if filepath .exists ():
33- filepath .unlink ()
65+ key = f"profile_pics/{ filename } "
66+ await run_in_threadpool (_delete_from_s3 , key )
0 commit comments