s3_storage.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. from collections.abc import Generator
  2. from contextlib import closing
  3. import boto3
  4. from botocore.client import Config
  5. from botocore.exceptions import ClientError
  6. from flask import Flask
  7. from extensions.storage.base_storage import BaseStorage
  8. class S3Storage(BaseStorage):
  9. """Implementation for s3 storage."""
  10. def __init__(self, app: Flask):
  11. super().__init__(app)
  12. app_config = self.app.config
  13. self.bucket_name = app_config.get("S3_BUCKET_NAME")
  14. if app_config.get("S3_USE_AWS_MANAGED_IAM"):
  15. session = boto3.Session()
  16. self.client = session.client("s3")
  17. else:
  18. self.client = boto3.client(
  19. "s3",
  20. aws_secret_access_key=app_config.get("S3_SECRET_KEY"),
  21. aws_access_key_id=app_config.get("S3_ACCESS_KEY"),
  22. endpoint_url=app_config.get("S3_ENDPOINT"),
  23. region_name=app_config.get("S3_REGION"),
  24. config=Config(s3={"addressing_style": app_config.get("S3_ADDRESS_STYLE")}),
  25. )
  26. # create bucket
  27. try:
  28. self.client.head_bucket(Bucket=self.bucket_name)
  29. except ClientError as e:
  30. # if bucket not exists, create it
  31. if e.response["Error"]["Code"] == "404":
  32. self.client.create_bucket(Bucket=self.bucket_name)
  33. # if bucket is not accessible, pass, maybe the bucket is existing but not accessible
  34. elif e.response["Error"]["Code"] == "403":
  35. pass
  36. else:
  37. # other error, raise exception
  38. raise
  39. def save(self, filename, data):
  40. self.client.put_object(Bucket=self.bucket_name, Key=filename, Body=data)
  41. def load_once(self, filename: str) -> bytes:
  42. try:
  43. with closing(self.client) as client:
  44. data = client.get_object(Bucket=self.bucket_name, Key=filename)["Body"].read()
  45. except ClientError as ex:
  46. if ex.response["Error"]["Code"] == "NoSuchKey":
  47. raise FileNotFoundError("File not found")
  48. else:
  49. raise
  50. return data
  51. def load_stream(self, filename: str) -> Generator:
  52. def generate(filename: str = filename) -> Generator:
  53. try:
  54. with closing(self.client) as client:
  55. response = client.get_object(Bucket=self.bucket_name, Key=filename)
  56. yield from response["Body"].iter_chunks()
  57. except ClientError as ex:
  58. if ex.response["Error"]["Code"] == "NoSuchKey":
  59. raise FileNotFoundError("File not found")
  60. else:
  61. raise
  62. return generate()
  63. def download(self, filename, target_filepath):
  64. with closing(self.client) as client:
  65. client.download_file(self.bucket_name, filename, target_filepath)
  66. def exists(self, filename):
  67. with closing(self.client) as client:
  68. try:
  69. client.head_object(Bucket=self.bucket_name, Key=filename)
  70. return True
  71. except:
  72. return False
  73. def delete(self, filename):
  74. self.client.delete_object(Bucket=self.bucket_name, Key=filename)