github.com/mundipagg/boleto-api@v0.0.0-20230620145841-3f9ec742599f/infrastructure/storage/azure_blob.go (about) 1 package storage 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "net/url" 8 "time" 9 10 "github.com/Azure/azure-storage-blob-go/azblob" 11 "github.com/mundipagg/boleto-api/config" 12 ) 13 14 // AzureBlob represents a AzureBlob connection to a named container 15 type AzureBlob struct { 16 accountName string 17 accessKey string 18 containerName string 19 containerURL azblob.ContainerURL 20 devMode bool 21 } 22 23 const MAX_UPLOAD_BLOCK_SIZE = 32 * 1024 * 1024 // 32MB 24 const CORE_MACHINE_NUMBER = 8 25 26 var jsonUploadOptions = azblob.UploadToBlockBlobOptions{ 27 BlockSize: MAX_UPLOAD_BLOCK_SIZE, 28 Parallelism: CORE_MACHINE_NUMBER, 29 } 30 31 // NewAzureBlob creates an instance of AzureBlob 32 // accountName is the AZURE_STORAGE_ACCOUNT 33 // accessKey is the AZURE_STORAGE_ACCESS_KEY 34 // containerName is the container name 35 func NewAzureBlob(accountName, accessKey, containerName string, devmode bool) (*AzureBlob, error) { 36 if len(accountName) == 0 || len(accessKey) == 0 || len(containerName) == 0 { 37 return nil, fmt.Errorf("either the AZURE_STORAGE_ACCOUNT, AZURE_STORAGE_ACCESS_KEY or Container name cannot be empty") 38 } 39 40 return &AzureBlob{ 41 accountName: accountName, 42 accessKey: accessKey, 43 containerName: containerName, 44 devMode: devmode, 45 }, nil 46 } 47 48 func (ab *AzureBlob) connect() error { 49 credential, err := azblob.NewSharedKeyCredential(ab.accountName, ab.accessKey) 50 if err != nil { 51 return fmt.Errorf("Invalid credentials with error: " + err.Error()) 52 } 53 54 p := azblob.NewPipeline(credential, azblob.PipelineOptions{}) 55 56 URL := ab.urlConnectionString() 57 58 // pipeline to make requests. 59 ab.containerURL = azblob.NewContainerURL(*URL, p) 60 61 return nil 62 } 63 64 // Download a file from blob 65 func (ab *AzureBlob) Download(path, filename string) ([]byte, error) { 66 ab.connect() 67 68 ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Second*20)) 69 defer cancel() 70 71 blobURL := ab.containerURL.NewBlockBlobURL(fmt.Sprintf("%s/%s", path, filename)) 72 downloadResponse, err := blobURL.Download( 73 ctx, 74 0, 75 azblob.CountToEnd, 76 azblob.BlobAccessConditions{}, 77 false, 78 azblob.ClientProvidedKeyOptions{}, 79 ) 80 if err != nil { 81 return []byte(""), err 82 } 83 84 bodyStream := downloadResponse.Body(azblob.RetryReaderOptions{MaxRetryRequests: 20}) 85 86 downloadedData := bytes.Buffer{} 87 _, err = downloadedData.ReadFrom(bodyStream) 88 if err != nil { 89 return []byte(""), err 90 } 91 92 return downloadedData.Bytes(), nil 93 } 94 95 func (ab *AzureBlob) urlConnectionString() *url.URL { 96 var URL *url.URL 97 if ab.devMode { 98 URL, _ = url.Parse(fmt.Sprintf("http://127.0.0.1:10000/%s/%s", ab.accountName, ab.containerName)) 99 } else { 100 URL, _ = url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", ab.accountName, ab.containerName)) 101 } 102 return URL 103 } 104 105 //UploadAsJson loads the payload into Azure Blob Storage 106 func (ab *AzureBlob) UploadAsJson(ctx context.Context, fileNamePrefix, payload string) (elapsedTime int64, err error) { 107 if err = ab.connect(); err != nil { 108 return 109 } 110 111 filename := fileNamePrefix + ".json" 112 fullpath := config.Get().AzureStorageUploadPath + "/" + config.Get().AzureStorageFallbackFolder + "/" + filename 113 114 elapsedTime, err = upload(ctx, ab, fullpath, payload) 115 116 return 117 } 118 119 func upload(ctx context.Context, ab *AzureBlob, fullpath, payload string) (elapsedTime int64, err error) { 120 data := []byte(payload) 121 122 blobURL := ab.containerURL.NewBlockBlobURL(fullpath) 123 124 start := time.Now() 125 126 _, err = azblob.UploadBufferToBlockBlob(ctx, data, blobURL, jsonUploadOptions) 127 128 elapsedTime = time.Since(start).Milliseconds() 129 130 return 131 }