201 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Microsoft.Extensions.Logging;
 | |
| 
 | |
| using MongoDB.Bson;
 | |
| using MongoDB.Driver;
 | |
| using MongoDB.Driver.GridFS;
 | |
| 
 | |
| using DomainResults.Common;
 | |
| 
 | |
| namespace DataProviders.Abstractions {
 | |
| 
 | |
|   /// <summary>
 | |
|   /// 
 | |
|   /// </summary>
 | |
|   /// <typeparam name="T"></typeparam>
 | |
|   public abstract class BucketDataProviderBase : DataProviderBase<BucketDataProviderBase> {
 | |
|  
 | |
|     /// <summary>
 | |
|     /// 
 | |
|     /// </summary>
 | |
|     /// <param name="logger"></param>
 | |
|     /// <param name="client"></param>
 | |
|     public BucketDataProviderBase(
 | |
|       ILogger<BucketDataProviderBase> logger,
 | |
|       IMongoClient client
 | |
|     ) : base (logger, client){ }
 | |
| 
 | |
|     #region Upload
 | |
|     private protected (Guid?, IDomainResult) Upload(Guid siteId, Guid userId, BucketFile file, string bucketName) =>
 | |
|       UploadAsync(siteId, userId, file, bucketName).Result;
 | |
| 
 | |
|     private protected Task<(Guid?, IDomainResult)> UploadAsync(Guid siteId, Guid userId, BucketFile file, string bucketName) =>
 | |
|       UploadAsyncCore(siteId, userId, file, bucketName);
 | |
|     #endregion
 | |
| 
 | |
|     #region Upload many
 | |
|     private protected (List<Guid>?, IDomainResult) UploadMany(Guid siteId, Guid userId, List<BucketFile> files, string bucketName) =>
 | |
|       UploadAsync(siteId, userId, files, bucketName).Result;
 | |
| 
 | |
|     private protected Task<(List<Guid>?, IDomainResult)> UploadAsync(Guid siteId, Guid userId, List<BucketFile> files, string bucketName) =>
 | |
|       UploadManyAsyncCore(siteId, userId, files, bucketName);
 | |
|     #endregion
 | |
| 
 | |
|     #region Download
 | |
|     private protected (List<BucketFile>?, IDomainResult) Download(FilterDefinition<GridFSFileInfo> filter, string bucketName) =>
 | |
|       DownloadAsync(filter, bucketName).Result;
 | |
| 
 | |
|     private protected Task<(List<BucketFile>?, IDomainResult)> DownloadAsync(FilterDefinition<GridFSFileInfo> filter, string bucketName) =>
 | |
|       DownloadAsyncCore(filter, bucketName);
 | |
|     #endregion
 | |
| 
 | |
|     #region Delete
 | |
|     private protected IDomainResult DeleteOne(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
 | |
|       DeleteOneAsync(siteId, userId, fileId, bucketName).Result;
 | |
| 
 | |
|     private protected Task<IDomainResult> DeleteOneAsync(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
 | |
|       DeleteOneAsyncCore(siteId, userId, fileId, bucketName);
 | |
|     #endregion
 | |
| 
 | |
|     #region Delete many
 | |
|     private protected IDomainResult DeleteMany(Guid siteId, Guid userId, string bucketName) =>
 | |
|       DeleteManyAsync(siteId, userId, bucketName).Result;
 | |
| 
 | |
|     private protected Task<IDomainResult> DeleteManyAsync(Guid siteId, Guid userId, string bucketName) =>
 | |
|       DeleteManyAsyncCore(siteId, userId, bucketName);
 | |
|     #endregion
 | |
| 
 | |
|     #region Core methods
 | |
|     private GridFSBucket CreateBucket(string bucketName) => new GridFSBucket(_client.GetDatabase(_databaseName), new GridFSBucketOptions {
 | |
|       BucketName = bucketName,
 | |
|       ChunkSizeBytes = 1048576, // 1MB
 | |
|       WriteConcern = WriteConcern.WMajority,
 | |
|       ReadPreference = ReadPreference.Secondary
 | |
|     });
 | |
| 
 | |
|     private async Task<(Guid?, IDomainResult)> UploadAsyncCore(Guid siteId, Guid userId, BucketFile file, string bucketName) {
 | |
|       var (list, result) = await UploadManyAsyncCore(siteId, userId, new List<BucketFile> { file }, bucketName);
 | |
| 
 | |
|       if (!result.IsSuccess || list == null)
 | |
|         return (null, result);
 | |
| 
 | |
|       return (list.First(), result);
 | |
|     }
 | |
|  
 | |
|     public async Task<(List<Guid>?, IDomainResult)> UploadManyAsyncCore(Guid siteId, Guid userId, List<BucketFile> files, string bucketName) {
 | |
|       var options = new GridFSUploadOptions {
 | |
|         ChunkSizeBytes = 64512, // 63KB
 | |
|       };
 | |
| 
 | |
|       try {
 | |
|         var bucket = CreateBucket(bucketName);
 | |
| 
 | |
|         var result = new List<Guid>();
 | |
|         foreach (var file in files) {
 | |
| 
 | |
|           options.Metadata = new BsonDocument {
 | |
|             { "siteId", $"{siteId}"},
 | |
|             { "userId", $"{userId}"},
 | |
|             { "fileName", file.Name },
 | |
|             { "contentType", file.ContentType },
 | |
|             { "private", true }
 | |
|           };
 | |
| 
 | |
|           var fileId = Guid.NewGuid();
 | |
|           await bucket.UploadFromBytesAsync($"{fileId}", file.Bytes, options);
 | |
|           result.Add(fileId);
 | |
|         }
 | |
| 
 | |
|         return result.Count > 0
 | |
|           ? IDomainResult.Success(result)
 | |
|           : IDomainResult.Failed<List<Guid>?>();
 | |
|       }
 | |
|       catch (Exception ex) {
 | |
|         _logger.LogError(ex, "Bucket data provider error");
 | |
|         return IDomainResult.Failed<List<Guid>?>();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     private async Task<(List<BucketFile>?, IDomainResult)> DownloadAsyncCore(FilterDefinition<GridFSFileInfo> filter, string bucketName) {
 | |
|       try {
 | |
|         var sort = Builders<GridFSFileInfo>.Sort.Descending(x => x.UploadDateTime);
 | |
| 
 | |
|         var bucket = CreateBucket(bucketName);
 | |
| 
 | |
|         using var cursor = await bucket.FindAsync(filter, new GridFSFindOptions {
 | |
|           Sort = sort,
 | |
|         });
 | |
| 
 | |
|         var result = new List<BucketFile>();
 | |
| 
 | |
|         // fileInfo either has the matching file information or is null
 | |
|         foreach (var fileInfo in await cursor.ToListAsync()) {
 | |
|           if (fileInfo == null)
 | |
|             return IDomainResult.NotFound<List<BucketFile>?>();
 | |
| 
 | |
|           var fileName = fileInfo.Metadata["fileName"].ToString() ?? "";
 | |
|           if (fileName == null)
 | |
|             return IDomainResult.Failed<List<BucketFile>?>();
 | |
| 
 | |
|           var contentType = fileInfo.Metadata["contentType"].ToString() ?? "";
 | |
|           if (contentType == null)
 | |
|             return IDomainResult.Failed<List<BucketFile>?>();
 | |
| 
 | |
|           var bytes = await bucket.DownloadAsBytesByNameAsync($"{fileInfo.Filename}", new GridFSDownloadByNameOptions {
 | |
|             Revision = -1
 | |
|           });
 | |
| 
 | |
|           if (bytes == null)
 | |
|             return IDomainResult.Failed<List<BucketFile>?>();
 | |
| 
 | |
|           result.Add(new BucketFile(fileName, bytes, contentType));
 | |
|         }
 | |
| 
 | |
|         return result.Count > 0
 | |
|           ? IDomainResult.Success(result)
 | |
|           : IDomainResult.NotFound<List<BucketFile>?>();
 | |
|        
 | |
|       }
 | |
|       catch (Exception ex) {
 | |
|         _logger.LogError(ex, "Bucket data provider error");
 | |
|         return IDomainResult.Failed<List<BucketFile>?>();
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     private Task<IDomainResult> DeleteOneAsyncCore(Guid siteId, Guid userId, Guid fileId, string bucketName) =>
 | |
|       DeleteAsyncCore(siteId, userId, Builders<GridFSFileInfo>.Filter.And(
 | |
|         Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
 | |
|         Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}"),
 | |
|         Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename, $"{fileId}")
 | |
|       ), bucketName);
 | |
| 
 | |
|     private Task<IDomainResult> DeleteManyAsyncCore(Guid siteId, Guid userId, string bucketName) =>
 | |
|       DeleteAsyncCore(siteId, userId, Builders<GridFSFileInfo>.Filter.And(
 | |
|         Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["siteId"], $"{siteId}"),
 | |
|         Builders<GridFSFileInfo>.Filter.Eq(x => x.Metadata["userId"], $"{userId}")
 | |
|       ), bucketName);
 | |
| 
 | |
|     private async Task<IDomainResult> DeleteAsyncCore(Guid siteId, Guid userId, FilterDefinition<GridFSFileInfo> filter, string bucketName) {
 | |
|       try {
 | |
|         var bucket = CreateBucket(bucketName);
 | |
| 
 | |
|         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
 | |
|   }
 | |
| }
 |