|  | @@ -2,9 +2,11 @@ import os
 | 
	
		
			
				|  |  |  import shutil
 | 
	
		
			
				|  |  |  from collections.abc import Generator
 | 
	
		
			
				|  |  |  from contextlib import closing
 | 
	
		
			
				|  |  | +from datetime import datetime, timedelta
 | 
	
		
			
				|  |  |  from typing import Union
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import boto3
 | 
	
		
			
				|  |  | +from azure.storage.blob import AccountSasPermissions, BlobServiceClient, ResourceTypes, generate_account_sas
 | 
	
		
			
				|  |  |  from botocore.exceptions import ClientError
 | 
	
		
			
				|  |  |  from flask import Flask
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -27,6 +29,18 @@ class Storage:
 | 
	
		
			
				|  |  |                  endpoint_url=app.config.get('S3_ENDPOINT'),
 | 
	
		
			
				|  |  |                  region_name=app.config.get('S3_REGION')
 | 
	
		
			
				|  |  |              )
 | 
	
		
			
				|  |  | +        elif self.storage_type == 'azure-blob':
 | 
	
		
			
				|  |  | +            self.bucket_name = app.config.get('AZURE_BLOB_CONTAINER_NAME')
 | 
	
		
			
				|  |  | +            sas_token = generate_account_sas(
 | 
	
		
			
				|  |  | +                account_name=app.config.get('AZURE_BLOB_ACCOUNT_NAME'),
 | 
	
		
			
				|  |  | +                account_key=app.config.get('AZURE_BLOB_ACCOUNT_KEY'),
 | 
	
		
			
				|  |  | +                resource_types=ResourceTypes(service=True, container=True, object=True),
 | 
	
		
			
				|  |  | +                permission=AccountSasPermissions(read=True, write=True, delete=True, list=True, add=True, create=True),
 | 
	
		
			
				|  |  | +                expiry=datetime.utcnow() + timedelta(hours=1)
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +            self.client = BlobServiceClient(account_url=app.config.get('AZURE_BLOB_ACCOUNT_URL'),
 | 
	
		
			
				|  |  | +                                            credential=sas_token)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              self.folder = app.config.get('STORAGE_LOCAL_PATH')
 | 
	
		
			
				|  |  |              if not os.path.isabs(self.folder):
 | 
	
	
		
			
				|  | @@ -35,6 +49,9 @@ class Storage:
 | 
	
		
			
				|  |  |      def save(self, filename, data):
 | 
	
		
			
				|  |  |          if self.storage_type == 's3':
 | 
	
		
			
				|  |  |              self.client.put_object(Bucket=self.bucket_name, Key=filename, Body=data)
 | 
	
		
			
				|  |  | +        elif self.storage_type == 'azure-blob':
 | 
	
		
			
				|  |  | +            blob_container = self.client.get_container_client(container=self.bucket_name)
 | 
	
		
			
				|  |  | +            blob_container.upload_blob(filename, data)
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              if not self.folder or self.folder.endswith('/'):
 | 
	
		
			
				|  |  |                  filename = self.folder + filename
 | 
	
	
		
			
				|  | @@ -63,6 +80,10 @@ class Storage:
 | 
	
		
			
				|  |  |                      raise FileNotFoundError("File not found")
 | 
	
		
			
				|  |  |                  else:
 | 
	
		
			
				|  |  |                      raise
 | 
	
		
			
				|  |  | +        elif self.storage_type == 'azure-blob':
 | 
	
		
			
				|  |  | +            blob = self.client.get_container_client(container=self.bucket_name)
 | 
	
		
			
				|  |  | +            blob = blob.get_blob_client(blob=filename)
 | 
	
		
			
				|  |  | +            data = blob.download_blob().readall()
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              if not self.folder or self.folder.endswith('/'):
 | 
	
		
			
				|  |  |                  filename = self.folder + filename
 | 
	
	
		
			
				|  | @@ -90,6 +111,11 @@ class Storage:
 | 
	
		
			
				|  |  |                          raise FileNotFoundError("File not found")
 | 
	
		
			
				|  |  |                      else:
 | 
	
		
			
				|  |  |                          raise
 | 
	
		
			
				|  |  | +            elif self.storage_type == 'azure-blob':
 | 
	
		
			
				|  |  | +                blob = self.client.get_blob_client(container=self.bucket_name, blob=filename)
 | 
	
		
			
				|  |  | +                with closing(blob.download_blob()) as blob_stream:
 | 
	
		
			
				|  |  | +                    while chunk := blob_stream.readall(4096):
 | 
	
		
			
				|  |  | +                        yield chunk
 | 
	
		
			
				|  |  |              else:
 | 
	
		
			
				|  |  |                  if not self.folder or self.folder.endswith('/'):
 | 
	
		
			
				|  |  |                      filename = self.folder + filename
 | 
	
	
		
			
				|  | @@ -109,6 +135,11 @@ class Storage:
 | 
	
		
			
				|  |  |          if self.storage_type == 's3':
 | 
	
		
			
				|  |  |              with closing(self.client) as client:
 | 
	
		
			
				|  |  |                  client.download_file(self.bucket_name, filename, target_filepath)
 | 
	
		
			
				|  |  | +        elif self.storage_type == 'azure-blob':
 | 
	
		
			
				|  |  | +            blob = self.client.get_blob_client(container=self.bucket_name, blob=filename)
 | 
	
		
			
				|  |  | +            with open(target_filepath, "wb") as my_blob:
 | 
	
		
			
				|  |  | +                blob_data = blob.download_blob()
 | 
	
		
			
				|  |  | +                blob_data.readinto(my_blob)
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              if not self.folder or self.folder.endswith('/'):
 | 
	
		
			
				|  |  |                  filename = self.folder + filename
 | 
	
	
		
			
				|  | @@ -128,6 +159,9 @@ class Storage:
 | 
	
		
			
				|  |  |                      return True
 | 
	
		
			
				|  |  |                  except:
 | 
	
		
			
				|  |  |                      return False
 | 
	
		
			
				|  |  | +        elif self.storage_type == 'azure-blob':
 | 
	
		
			
				|  |  | +            blob = self.client.get_blob_client(container=self.bucket_name, blob=filename)
 | 
	
		
			
				|  |  | +            return blob.exists()
 | 
	
		
			
				|  |  |          else:
 | 
	
		
			
				|  |  |              if not self.folder or self.folder.endswith('/'):
 | 
	
		
			
				|  |  |                  filename = self.folder + filename
 |