Webiant Logo Webiant Logo
  1. No results found.

    Try your search with a different keyword or use * as a wildcard.

AzurePictureService.cs

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.AspNetCore.Http;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Configuration;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Media;
using Nop.Core.Infrastructure;
using Nop.Data;
using Nop.Services.Catalog;
using Nop.Services.Configuration;
using Nop.Services.Logging;
using Nop.Services.Seo;

namespace Nop.Services.Media;

/// 
/// Picture service for Windows Azure
/// 
public partial class AzurePictureService : PictureService
{
    #region Fields

    protected static BlobContainerClient _blobContainerClient;
    protected static BlobServiceClient _blobServiceClient;
    protected static bool _azureBlobStorageAppendContainerName;
    protected static bool _isInitialized;
    protected static string _azureBlobStorageConnectionString;
    protected static string _azureBlobStorageContainerName;
    protected static string _azureBlobStorageEndPoint;

    protected readonly IStaticCacheManager _staticCacheManager;

    protected readonly object _locker = new();

    #endregion

    #region Ctor

    public AzurePictureService(AppSettings appSettings,
        IDownloadService downloadService,
        IHttpContextAccessor httpContextAccessor,
        ILogger logger,
        INopFileProvider fileProvider,
        IProductAttributeParser productAttributeParser,
        IProductAttributeService productAttributeService,
        IRepository pictureRepository,
        IRepository pictureBinaryRepository,
        IRepository productPictureRepository,
        ISettingService settingService,
        IStaticCacheManager staticCacheManager,
        IUrlRecordService urlRecordService,
        IWebHelper webHelper,
        MediaSettings mediaSettings)
        : base(downloadService,
            httpContextAccessor,
            logger,
            fileProvider,
            productAttributeParser,
            productAttributeService,
            pictureRepository,
            pictureBinaryRepository,
            productPictureRepository,
            settingService,
            urlRecordService,
            webHelper,
            mediaSettings)
    {
        _staticCacheManager = staticCacheManager;

        OneTimeInit(appSettings);
    }

    #endregion

    #region Utilities

    /// 
    /// Initialize cloud container
    /// 
    /// App settings
    protected void OneTimeInit(AppSettings appSettings)
    {
        if (_isInitialized)
            return;

        if (string.IsNullOrEmpty(appSettings.Get().ConnectionString))
            throw new Exception("Azure connection string for Blob is not specified");

        if (string.IsNullOrEmpty(appSettings.Get().ContainerName))
            throw new Exception("Azure container name for Blob is not specified");

        if (string.IsNullOrEmpty(appSettings.Get().EndPoint))
            throw new Exception("Azure end point for Blob is not specified");

        lock (_locker)
        {
            if (_isInitialized)
                return;

            _azureBlobStorageAppendContainerName = appSettings.Get().AppendContainerName;
            _azureBlobStorageConnectionString = appSettings.Get().ConnectionString;
            _azureBlobStorageContainerName = appSettings.Get().ContainerName.Trim().ToLowerInvariant();
            _azureBlobStorageEndPoint = appSettings.Get().EndPoint.Trim().ToLowerInvariant().TrimEnd('/');

            _blobServiceClient = new BlobServiceClient(_azureBlobStorageConnectionString);
            _blobContainerClient = _blobServiceClient.GetBlobContainerClient(_azureBlobStorageContainerName);

            CreateCloudBlobContainer().GetAwaiter().GetResult();

            _isInitialized = true;
        }
    }

    /// 
    /// Create cloud Blob container
    /// 
    /// A task that represents the asynchronous operation
    protected virtual async Task CreateCloudBlobContainer()
    {
        await _blobContainerClient.CreateIfNotExistsAsync(PublicAccessType.Blob);
    }

    /// 
    /// Get picture (thumb) local path
    /// 
    /// Filename
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the local picture thumb path
    /// 
    protected override Task GetThumbLocalPathAsync(string thumbFileName)
    {
        var path = _azureBlobStorageAppendContainerName ? $"{_azureBlobStorageContainerName}/" : string.Empty;

        return Task.FromResult($"{_azureBlobStorageEndPoint}/{path}{thumbFileName}");
    }

    /// 
    /// Get picture (thumb) URL 
    /// 
    /// Filename
    /// Store location URL; null to use determine the current store location automatically
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the local picture thumb path
    /// 
    protected override async Task GetThumbUrlAsync(string thumbFileName, string storeLocation = null)
    {
        return await GetThumbLocalPathAsync(thumbFileName);
    }

    /// 
    /// Initiates an asynchronous operation to delete picture thumbs
    /// 
    /// Picture
    /// A task that represents the asynchronous operation
    protected override async Task DeletePictureThumbsAsync(Picture picture)
    {
        //create a string containing the Blob name prefix
        var prefix = $"{picture.Id:0000000}";

        var tasks = new List();
        await foreach (var blob in _blobContainerClient.GetBlobsAsync(BlobTraits.All, BlobStates.All, prefix))
        {
            tasks.Add(_blobContainerClient.DeleteBlobIfExistsAsync(blob.Name, DeleteSnapshotsOption.IncludeSnapshots));
        }
        await Task.WhenAll(tasks);

        await _staticCacheManager.RemoveByPrefixAsync(NopMediaDefaults.ThumbsExistsPrefix);
    }

    /// 
    /// Initiates an asynchronous operation to get a value indicating whether some file (thumb) already exists
    /// 
    /// Thumb file path
    /// Thumb file name
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the result
    /// 
    protected override async Task GeneratedThumbExistsAsync(string thumbFilePath, string thumbFileName)
    {
        try
        {
            var key = _staticCacheManager.PrepareKeyForDefaultCache(NopMediaDefaults.ThumbExistsCacheKey, thumbFileName);

            return await _staticCacheManager.GetAsync(key, async () =>
            {
                return await _blobContainerClient.GetBlobClient(thumbFileName).ExistsAsync();
            });
        }
        catch
        {
            return false;
        }
    }

    /// 
    /// Initiates an asynchronous operation to save a value indicating whether some file (thumb) already exists
    /// 
    /// Thumb file path
    /// Thumb file name
    /// MIME type
    /// Picture binary
    /// A task that represents the asynchronous operation
    protected override async Task SaveThumbAsync(string thumbFilePath, string thumbFileName, string mimeType, byte[] binary)
    {
        var blobClient = _blobContainerClient.GetBlobClient(thumbFileName);
        await using var ms = new MemoryStream(binary);

        //set mime type
        BlobHttpHeaders headers = null;
        if (!string.IsNullOrWhiteSpace(mimeType))
        {
            headers = new BlobHttpHeaders
            {
                ContentType = mimeType
            };
        }

        //set cache control
        if (!string.IsNullOrWhiteSpace(_mediaSettings.AzureCacheControlHeader))
        {
            headers ??= new BlobHttpHeaders();
            headers.CacheControl = _mediaSettings.AzureCacheControlHeader;
        }

        if (headers is null)
            //We must explicitly indicate through the parameter that the object needs to be overwritten if it already exists
            //See more: https://github.com/Azure/azure-sdk-for-net/issues/9470
            await blobClient.UploadAsync(ms, overwrite: true);
        else
            await blobClient.UploadAsync(ms, new BlobUploadOptions { HttpHeaders = headers });

        await _staticCacheManager.RemoveByPrefixAsync(NopMediaDefaults.ThumbsExistsPrefix);
    }

    #endregion
}