175 lines
6.1 KiB
Python
175 lines
6.1 KiB
Python
import boto3
|
|
import hashlib
|
|
import os
|
|
import logging
|
|
from app.extensions import redis_controller
|
|
from config import Config
|
|
|
|
Config = Config()
|
|
|
|
def getS3Client():
|
|
"""
|
|
Returns a boto3 S3 client object.
|
|
"""
|
|
|
|
return boto3.client(
|
|
"s3",
|
|
aws_access_key_id = Config.AWS_ACCESS_KEY,
|
|
aws_secret_access_key = Config.AWS_SECRET_KEY,
|
|
region_name = Config.AWS_REGION_NAME
|
|
)
|
|
|
|
def UploadBytesToS3( fileContent : bytes, nameOverwrite : str | None = None, bucketOverwrite : str | None = Config.AWS_S3_BUCKET_NAME, contentType : str = "application/octet-stream", bypassExistCheck : bool = False) -> str:
|
|
"""
|
|
Uploads a file to S3, returning the URL of the file.
|
|
|
|
:param fileContent: The bytes of the file to upload
|
|
:param nameOverwrite: The name of the file to overwrite
|
|
:param bucketOverwrite: The bucket to overwrite
|
|
:param contentType: The content type of the file
|
|
:param bypassExistCheck: Whether to bypass the redis cache or not
|
|
|
|
:returns: str (The URL of the file)
|
|
"""
|
|
|
|
if nameOverwrite is None:
|
|
nameOverwrite = hashlib.sha512( fileContent ).hexdigest()
|
|
if Config.USE_LOCAL_STORAGE:
|
|
if not os.path.exists(Config.AWS_S3_DOWNLOAD_CACHE_DIR + "/" + nameOverwrite):
|
|
with open(Config.AWS_S3_DOWNLOAD_CACHE_DIR + "/" + nameOverwrite, "wb") as f:
|
|
f.write(fileContent)
|
|
return f"{Config.CDN_URL}/{nameOverwrite}"
|
|
|
|
if not bypassExistCheck:
|
|
if redis_controller.exists( f"DoesKeyExist:{bucketOverwrite}:{nameOverwrite}" ):
|
|
return f"{Config.CDN_URL}/{nameOverwrite}"
|
|
|
|
s3Client = getS3Client()
|
|
|
|
s3Client.put_object(
|
|
Body = fileContent,
|
|
Bucket = bucketOverwrite,
|
|
Key = nameOverwrite,
|
|
ContentType = contentType
|
|
)
|
|
redis_controller.set( f"DoesKeyExist:{bucketOverwrite}:{nameOverwrite}", "1", 60 * 60 * 24 * 7 )
|
|
return f"{Config.CDN_URL}/{nameOverwrite}"
|
|
|
|
def UploadFileToS3( filePath : str, nameOverwrite : str | None = None, bucketOverwrite : str | None = Config.AWS_S3_BUCKET_NAME ) -> str:
|
|
"""
|
|
Uploads a file to S3, returning the URL of the file.
|
|
|
|
:param filePath: The path of the file to upload
|
|
:param nameOverwrite: The name of the file to overwrite
|
|
:param bucketOverwrite: The bucket to overwrite
|
|
|
|
:returns: str (The URL of the file)
|
|
"""
|
|
|
|
if nameOverwrite is None:
|
|
nameOverwrite = hashlib.sha512( open( filePath, "rb" ).read() ).hexdigest()
|
|
|
|
if Config.USE_LOCAL_STORAGE:
|
|
FileData = open(filePath, "rb").read()
|
|
return UploadBytesToS3(FileData, nameOverwrite, bucketOverwrite, bypassExistCheck=True)
|
|
|
|
s3Client = getS3Client()
|
|
|
|
s3Client.upload_file(
|
|
filePath,
|
|
bucketOverwrite,
|
|
nameOverwrite
|
|
)
|
|
redis_controller.set( f"DoesKeyExist:{bucketOverwrite}:{nameOverwrite}", "1", 60 * 60 * 24 * 7 )
|
|
return f"{Config.CDN_URL}/{nameOverwrite}"
|
|
|
|
def DeleteFileFromS3( fileName : str, bucketOverwrite : str | None = Config.AWS_S3_BUCKET_NAME ) -> bool:
|
|
"""
|
|
Deletes a file from S3.
|
|
|
|
:param fileName: The name of the file to delete
|
|
:param bucketOverwrite: The bucket to overwrite
|
|
|
|
:returns: bool (Whether the file was deleted or not)
|
|
"""
|
|
|
|
if Config.USE_LOCAL_STORAGE:
|
|
if os.path.exists(f"{Config.AWS_S3_DOWNLOAD_CACHE_DIR}/{fileName}"):
|
|
os.remove(f"{Config.AWS_S3_DOWNLOAD_CACHE_DIR}/{fileName}")
|
|
return True
|
|
|
|
s3Client = getS3Client()
|
|
|
|
s3Client.delete_object(
|
|
Bucket = bucketOverwrite,
|
|
Key = fileName
|
|
)
|
|
redis_controller.delete( f"DoesKeyExist:{bucketOverwrite}:{fileName}" )
|
|
return True
|
|
|
|
def GetFileFromS3( fileName : str, bucketOverwrite : str | None = Config.AWS_S3_BUCKET_NAME, skipDownloadCache : bool = False ) -> bytes | None:
|
|
"""
|
|
Gets a file from S3.
|
|
|
|
:param fileName: The name of the file to get
|
|
:param bucketOverwrite: The bucket to overwrite
|
|
:param skipDownloadCache: Whether to skip the download cache or not
|
|
|
|
:returns: bytes (The bytes of the file)
|
|
"""
|
|
|
|
if not skipDownloadCache or Config.USE_LOCAL_STORAGE:
|
|
if redis_controller.exists(f"DownloadCache:{bucketOverwrite}:{fileName}") and os.path.exists(f"{Config.AWS_S3_DOWNLOAD_CACHE_DIR}/{fileName}"):
|
|
return open(f"{Config.AWS_S3_DOWNLOAD_CACHE_DIR}/{fileName}", "rb").read()
|
|
if Config.USE_LOCAL_STORAGE:
|
|
return None
|
|
|
|
s3Client = getS3Client()
|
|
|
|
try:
|
|
FileBytes = s3Client.get_object(
|
|
Bucket = bucketOverwrite,
|
|
Key = fileName
|
|
)['Body'].read()
|
|
except Exception as e:
|
|
logging.error(f"S3Helper.GetFileFromS3 / Exception raised when fetching file from S3 Bucket [ {bucketOverwrite} ] with name [ {fileName} ] / {e}]")
|
|
return None
|
|
|
|
if not os.path.exists(Config.AWS_S3_DOWNLOAD_CACHE_DIR):
|
|
os.mkdir(Config.AWS_S3_DOWNLOAD_CACHE_DIR)
|
|
with open(f"{Config.AWS_S3_DOWNLOAD_CACHE_DIR}/{fileName}", "wb") as f:
|
|
f.write(FileBytes)
|
|
redis_controller.set(f"DownloadCache:{bucketOverwrite}:{fileName}", "1", Config.AWS_S3_CACHE_LIFETIME)
|
|
|
|
return FileBytes
|
|
|
|
def DoesKeyExist( key : str, bucketOverwrite : str | None = Config.AWS_S3_BUCKET_NAME, bypassCache : bool = False ) -> bool:
|
|
"""
|
|
Returns whether or not a key exists in S3.
|
|
|
|
:param key: The key to check
|
|
:param bucketOverwrite: The bucket to overwrite
|
|
:param bypassCache: Whether to bypass the redis cache or not
|
|
|
|
:returns: bool (Whether the key exists or not)
|
|
"""
|
|
|
|
if Config.USE_LOCAL_STORAGE:
|
|
return os.path.exists(f"{Config.AWS_S3_DOWNLOAD_CACHE_DIR}/{key}")
|
|
|
|
if not bypassCache:
|
|
if redis_controller.exists( f"DoesKeyExist:{bucketOverwrite}:{key}" ):
|
|
return True
|
|
s3Client = getS3Client()
|
|
|
|
try:
|
|
s3Client.head_object(
|
|
Bucket = bucketOverwrite,
|
|
Key = key
|
|
)
|
|
except:
|
|
redis_controller.delete( f"DoesKeyExist:{bucketOverwrite}:{key}" )
|
|
return False
|
|
|
|
redis_controller.set( f"DoesKeyExist:{bucketOverwrite}:{key}", "1", 60 * 60 * 24 * 7 )
|
|
return True |