using Microsoft.Extensions.Logging; using MongoDB.Bson; using MongoDB.Driver; using MongoDB.Driver.GridFS; using DomainResults.Common; using ExtensionMethods; namespace DataProviders.Abstractions { /// /// /// /// public abstract class BucketDataProviderBase : DataProviderBase { private readonly GridFSBucket _bucket; /// /// /// /// /// public BucketDataProviderBase( ILogger logger, IMongoClient client, string bucketName ) : base (logger, client, "reactredux") { _bucket = new GridFSBucket(_database, new GridFSBucketOptions { BucketName = bucketName, ChunkSizeBytes = 1048576, // 1MB WriteConcern = WriteConcern.WMajority, ReadPreference = ReadPreference.Secondary }); } #region Upload public (Guid?, IDomainResult) Upload(BucketFile file) => UploadAsync(file).Result; public async Task<(Guid?, IDomainResult)> UploadAsync(BucketFile file) { var (list, result) = await UploadManyAsync(new List { file }); if (!result.IsSuccess || list == null) return (null, result); return (list.First(), result); } #endregion #region Upload many public (List?, IDomainResult) UploadMany(List files) => UploadManyAsync(files).Result; public async Task<(List?, IDomainResult)> UploadManyAsync(List files) { var options = new GridFSUploadOptions { ChunkSizeBytes = 64512, // 63KB }; try { var result = new List(); foreach (var file in files) { options.Metadata = new BsonDocument { { "siteId", $"{file.SiteId}"}, { "userId", $"{file.UserId}"}, { "published", file.Published.ToString() }, { "fileName", file.Name }, { "contentType", file.ContentType } }; await _bucket.UploadFromBytesAsync($"{file.Id}", file.Bytes, options); result.Add(file.Id); } return result.Count > 0 ? IDomainResult.Success(result) : IDomainResult.Failed?>(); } catch (Exception ex) { _logger.LogError(ex, "Bucket data provider error"); return IDomainResult.Failed?>(); } } #endregion #region Download private protected (List?, IDomainResult) Download(FilterDefinition filter) => DownloadAsync(filter).Result; private protected async Task<(List?, IDomainResult)> DownloadAsync(FilterDefinition filter) { try { var sort = Builders.Sort.Descending(x => x.UploadDateTime); using var cursor = await _bucket.FindAsync(filter, new GridFSFindOptions { Sort = sort, }); var result = new List(); // fileInfo either has the matching file information or is null foreach (var fileInfo in await cursor.ToListAsync()) { if (fileInfo == null) return IDomainResult.NotFound?>(); var id = fileInfo.Filename.ToGuid(); var userId = fileInfo.Metadata["userId"].ToString()?.ToNullableGuid(); var siteId = fileInfo.Metadata["siteId"].ToString()?.ToNullableGuid(); var published = fileInfo.Metadata["published"].ToString()?.ToNullableDateTime(); var fileName = fileInfo.Metadata["fileName"].ToString() ?? ""; var bytes = await _bucket.DownloadAsBytesByNameAsync($"{fileInfo.Filename}", new GridFSDownloadByNameOptions { Revision = -1 }); var contentType = fileInfo.Metadata["contentType"].ToString() ?? ""; if (siteId == null || userId == null || bytes == null) return IDomainResult.Failed?>(); result.Add(new BucketFile(id, siteId.Value, userId.Value, published, fileName, bytes, contentType)); } return result.Count > 0 ? IDomainResult.Success(result) : IDomainResult.NotFound?>(); } catch (Exception ex) { _logger.LogError(ex, "Bucket data provider error"); return IDomainResult.Failed?>(); } } #endregion #region Delete private protected IDomainResult Delete(FilterDefinition filter) => DeleteAsync(filter).Result; private protected async Task DeleteAsync(FilterDefinition filter) { try { using var cursor = await _bucket.FindAsync(filter); var count = 0; foreach (var fileInfo in await cursor.ToListAsync()) { await _bucket.DeleteAsync(fileInfo.Id); count++; } return count > 0 ? IDomainResult.Success() : IDomainResult.NotFound(); } catch (Exception ex) { _logger.LogError(ex, "Bucket data provider error"); return IDomainResult.Failed(); } } #endregion } }