241 lines
8.7 KiB
C#
241 lines
8.7 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Picture service for Windows Azure
|
|
/// </summary>
|
|
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<Picture> pictureRepository,
|
|
IRepository<PictureBinary> pictureBinaryRepository,
|
|
IRepository<ProductPicture> 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
|
|
|
|
/// <summary>
|
|
/// Initialize cloud container
|
|
/// </summary>
|
|
/// <param name="appSettings">App settings</param>
|
|
protected void OneTimeInit(AppSettings appSettings)
|
|
{
|
|
if (_isInitialized)
|
|
return;
|
|
|
|
if (string.IsNullOrEmpty(appSettings.Get<AzureBlobConfig>().ConnectionString))
|
|
throw new Exception("Azure connection string for Blob is not specified");
|
|
|
|
if (string.IsNullOrEmpty(appSettings.Get<AzureBlobConfig>().ContainerName))
|
|
throw new Exception("Azure container name for Blob is not specified");
|
|
|
|
if (string.IsNullOrEmpty(appSettings.Get<AzureBlobConfig>().EndPoint))
|
|
throw new Exception("Azure end point for Blob is not specified");
|
|
|
|
lock (_locker)
|
|
{
|
|
if (_isInitialized)
|
|
return;
|
|
|
|
_azureBlobStorageAppendContainerName = appSettings.Get<AzureBlobConfig>().AppendContainerName;
|
|
_azureBlobStorageConnectionString = appSettings.Get<AzureBlobConfig>().ConnectionString;
|
|
_azureBlobStorageContainerName = appSettings.Get<AzureBlobConfig>().ContainerName.Trim().ToLowerInvariant();
|
|
_azureBlobStorageEndPoint = appSettings.Get<AzureBlobConfig>().EndPoint.Trim().ToLowerInvariant().TrimEnd('/');
|
|
|
|
_blobServiceClient = new BlobServiceClient(_azureBlobStorageConnectionString);
|
|
_blobContainerClient = _blobServiceClient.GetBlobContainerClient(_azureBlobStorageContainerName);
|
|
|
|
CreateCloudBlobContainer().GetAwaiter().GetResult();
|
|
|
|
_isInitialized = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create cloud Blob container
|
|
/// </summary>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
protected virtual async Task CreateCloudBlobContainer()
|
|
{
|
|
await _blobContainerClient.CreateIfNotExistsAsync(PublicAccessType.Blob);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get picture (thumb) local path
|
|
/// </summary>
|
|
/// <param name="thumbFileName">Filename</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the local picture thumb path
|
|
/// </returns>
|
|
protected override Task<string> GetThumbLocalPathAsync(string thumbFileName)
|
|
{
|
|
var path = _azureBlobStorageAppendContainerName ? $"{_azureBlobStorageContainerName}/" : string.Empty;
|
|
|
|
return Task.FromResult($"{_azureBlobStorageEndPoint}/{path}{thumbFileName}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get picture (thumb) URL
|
|
/// </summary>
|
|
/// <param name="thumbFileName">Filename</param>
|
|
/// <param name="storeLocation">Store location URL; null to use determine the current store location automatically</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the local picture thumb path
|
|
/// </returns>
|
|
protected override async Task<string> GetThumbUrlAsync(string thumbFileName, string storeLocation = null)
|
|
{
|
|
return await GetThumbLocalPathAsync(thumbFileName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initiates an asynchronous operation to delete picture thumbs
|
|
/// </summary>
|
|
/// <param name="picture">Picture</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
protected override async Task DeletePictureThumbsAsync(Picture picture)
|
|
{
|
|
//create a string containing the Blob name prefix
|
|
var prefix = $"{picture.Id:0000000}";
|
|
|
|
var tasks = new List<Task>();
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initiates an asynchronous operation to get a value indicating whether some file (thumb) already exists
|
|
/// </summary>
|
|
/// <param name="thumbFilePath">Thumb file path</param>
|
|
/// <param name="thumbFileName">Thumb file name</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the result
|
|
/// </returns>
|
|
protected override async Task<bool> 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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initiates an asynchronous operation to save a value indicating whether some file (thumb) already exists
|
|
/// </summary>
|
|
/// <param name="thumbFilePath">Thumb file path</param>
|
|
/// <param name="thumbFileName">Thumb file name</param>
|
|
/// <param name="mimeType">MIME type</param>
|
|
/// <param name="binary">Picture binary</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
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
|
|
} |