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  }