using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders; using xCommons.Extensions; using xDataService.Models; using xExceptions.Constants; using xFileService.Extensions; using xFileService.Interfaces; using xFileService.Interfaces.Dtos; using xFileService.Models.Dtos; using xFileService.Models.Entities; using xFileService.Providers.Dtos; using xIdentityModels.Models; using xStorageService.Interfaces; using xTagService.Providers; namespace xFileService.Providers { public class XFileProvider : XBaseProvidedHasTag, IXFileProvider { private readonly IXFileServiceProvider provider; private readonly IXStorageProvider storageProvider; public XFileProvider( IXFileTagProvider tagProvider, IXFileServiceProvider provider, IXStorageProvider storageProvider ) : base( provider: provider, tagProvider: tagProvider, permittedRoles: new string[] { "admin" } ) { this.provider = provider; this.storageProvider = storageProvider; } public override bool IsOwned( XFile item, XUserClaimsInfoDto userInfo ) { // var result = !item.IsNullOrDefault() && !userInfo.IsNullOrDefault() && !item.OwnerId.IsNullOrEmpty() && userInfo.UserId == item.OwnerId; // return result; } public override bool IsOwned( XFileDto item, XUserClaimsInfoDto userInfo ) { // var result = !item.IsNullOrDefault() && !userInfo.IsNullOrDefault() && !item.OwnerId.IsNullOrEmpty() && userInfo.UserId == item.OwnerId; // return result; } // #region Tools ... /// /// Stream Specified File ... /// /// /// /// public virtual async Task Stream( Guid id, CancellationToken cancellationToken = default ) { // // Validate Args ... var isValid = !id.IsNull() && !id.IsDefaultGuid(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Generate Stream Info and Validate it ... var stream = await GetFileDescriptor( id: id, cancellationToken: cancellationToken ); if (stream.IsNull()) { XException.NotFound.Throw(); } // // Generate Result Model ... var result = new FileStreamResult( stream.Stream, stream.MIMEType ) { FileDownloadName = stream.Name }; // return result; } /// /// Stream Specified File ... /// /// /// /// public virtual async Task Stream( string name, CancellationToken cancellationToken = default ) { // // Validate Args ... var isValid = !name.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Generate Stream Info and Validate it ... var stream = await GetFileDescriptor( fileName: name, cancellationToken: cancellationToken ); if (stream.IsNull()) { XException.NotFound.Throw(); } // // Generate Result Model ... var result = new FileStreamResult( stream.Stream, stream.MIMEType ) { FileDownloadName = stream.Name }; // return result; } /// /// Download Specified File ... /// /// /// /// public virtual async Task Download( Guid id, CancellationToken cancellationToken = default ) { // // Validate Args ... var isValid = !id.IsNull() && !id.IsDefaultGuid(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Generate Stream Info and Validate it ... var stream = await GetFileDescriptor( id: id, cancellationToken: cancellationToken ); if (stream.IsNull()) { XException.NotFound.Throw(); } // // Generate Result Model ... var result = new PhysicalFileResult( stream.Path, stream.MIMEType ) { FileDownloadName = stream.Name }; // return result; } /// /// Download Specified File ... /// /// /// /// public virtual async Task Download( string name, CancellationToken cancellationToken = default ) { // // Validate Args ... var isValid = !name.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Generate Stream Info and Validate it ... var stream = await GetFileDescriptor( fileName: name, cancellationToken: cancellationToken ); if (stream.IsNull()) { XException.NotFound.Throw(); } // // Generate Result Model ... var result = new PhysicalFileResult( stream.Path, stream.MIMEType ) { FileDownloadName = stream.Name }; // return result; } /// /// Upload Files ... /// /// /// /// /// /// public virtual async Task> Upload( IFormFileCollection files, XUserClaimsInfoDto userInfo = null, string connectionId = null, CancellationToken cancellationToken = default ) { // // Validate Args ... var isValid = !files.IsNull() && files.HasChild() && !userInfo.IsNullOrDefault(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Save Files On Server Side Physically ... // and Validate it ... var uploadResults = await storageProvider .HandleFilesSave(files); isValid = uploadResults.HasChild(); if (!isValid) { XException.ActionFailed.Throw(); } // // Prepare Result ... var result = new List(); foreach (var ur in uploadResults) { // var fileType = storageProvider.GetFileType(ur.FileName); // // Create Item Model ... var item = new XFileDto { Type = fileType, FileName = ur.Name, Name = ur.FileName, Path = ur.FilePath, Thumb = ur.Thmbnail, OwnerId = userInfo.UserId, ThumbPath = ur.ThmbnailPath, UploadedOn = DateTime.UtcNow, }; // try { // // Add Entity Model to DB ... item = await Add( item: item, userInfo: userInfo, connectionId: connectionId, cancellationToken: cancellationToken ); // result.Add(item); } catch { } } // return result; } /// /// Remove Specified Files ... /// /// /// /// /// /// public virtual async Task> Remove( string ids, XUserClaimsInfoDto userInfo = null, string connectionId = null, CancellationToken cancellationToken = default ) { // // Validate Args ... var isValid = !ids.IsNullOrEmpty() && !userInfo.IsNullOrDefault(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Parse ID(s) List ... var idsList = ids.ParseListGuid(); isValid = !idsList.IsNull() && idsList.HasChild(); if (!isValid) { XException.ActionFailed.Throw(); } // // Loop Through Parsed Ids and Try to Remove ... var result = new List(); foreach (var id in idsList) { // // Retrieve XFileDto and Validate it ... var model = await Get(id); isValid = !model.IsNull() && !model.Id.IsNull() && !model.Id.IsDefaultGuid(); if (!isValid) { continue; } // // Check Physical File Exists or not ... var fileFullPath = storageProvider.GetFileFullPath(model.Name); var thumbFullPath = model.Thumb.IsNullOrEmpty() ? "" : storageProvider.GetThumbnailFileFullPath(model.Thumb); var isFileExists = storageProvider.IsFileExists(fileFullPath); var isThumbExists = model.Thumb.IsNullOrEmpty() ? false : storageProvider.IsFileExists(thumbFullPath); // // Remove Physical Thumbnail File ... if (isThumbExists) { // try { // // Removed Stored Thumbnail ... storageProvider.DeleteFile( fileFullPath: thumbFullPath, forceFileExists: false ); } catch { } } // // Remove Pgysical File ... if (isFileExists) { // try { // // Remove Stored File ... storageProvider.DeleteFile( fileFullPath: fileFullPath, forceFileExists: false ); // // Remove Tags ... await DetachTags( id: id, userInfo: userInfo, connectionId: connectionId, cancellationToken: cancellationToken ); } catch { } } // // Remove File Entty ... var item = await provider.RemoveAsync( id: model.Id, softDelete: false, saveChanges: true, connectionId: connectionId, cancellationToken: cancellationToken ); result.Add(model.Id); } // return result; } /// /// Retrieve Specified File Streaming Info ... /// /// /// /// public virtual async Task GetFileDescriptor( Guid id, CancellationToken cancellationToken = default ) { // // Check File Model Exists or not ... var isExists = await IsExists( id: id, cancellationToken: cancellationToken ); if (!isExists) { XException.NotFound.Throw(); } // // Retrieve Item and Validate it ... var item = await Get( id: id, includeBuilder: null, cancellationToken: cancellationToken ); if (item.IsNullOrDefault()) { XException.NotFound.Throw(); } // // Generate Physical File Path and Validate it ... var fileFullPath = storageProvider.GetFileFullPath(item.Name); isExists = storageProvider.IsFileExists(fileFullPath); var filePath = storageProvider.FilePathResolver(fileFullPath); filePath = Path.Combine("wwwroot", filePath); if (!isExists) { XException.NotFound.Throw(); } // // Generate Physical File Info ... IFileInfo fileInfo = storageProvider.FileProvider.GetFileInfo(filePath); // // Prepare Default Streaming Requirements ... var mimeType = "application/octetstream"; var readStream = fileInfo.CreateReadStream(); var fileMimeProvider = new FileExtensionContentTypeProvider(); var fileName = item.Name; // // Try to Find Physical File's Content Type ... fileMimeProvider.TryGetContentType( filePath, out mimeType ); // // Prepare Result Model ... var result = new XFileStreamDescriptorDto { Stream = readStream, MIMEType = mimeType, Name = fileName, Path = fileFullPath }; // return result; } /// /// Retrieve Specified File Streaming Info ... /// /// /// /// public virtual async Task GetFileDescriptor( string fileName, CancellationToken cancellationToken = default ) { // if (fileName.IsNullOrEmpty()) { XException.InvalidArgs.Throw(); } var item = await FindOne( includeBuilder: null, predicate: e => e.Name.Contains(fileName) || e.Path.Contains(fileName) || e.Thumb.Contains(fileName) || e.FileName.Contains(fileName) || e.ThumbPath.Contains(fileName), cancellationToken: cancellationToken ); var isExists = !item.IsNullOrDefault(); if (!isExists) { XException.NotFound.Throw(); } // // Extract File Name ... fileName = Path.GetFileName(fileName); // // Check File is Thumbnail or File ... var isThumb = fileName.StartsWith(storageProvider.Configuration.ThumbPrefix); var isFile = fileName.StartsWith(storageProvider.Configuration.FilePrefix); // // Check File Type is Valid ... isExists = isThumb || isFile; if (!isExists) { XException.UnsupportedFileType.Throw(); } // // Extract File Full Path ... var fileFullPath = ""; if (isThumb) { fileFullPath = storageProvider.GetThumbnailFileFullPath(fileName); } else { fileFullPath = storageProvider.GetFileFullPath(fileName); } // // Check Physical File Exists or Not ... isExists = storageProvider.IsFileExists(fileFullPath); if (!isExists) { XException.NotFound.Throw(); } // // Generate Physical File Info ... var filePath = storageProvider.FilePathResolver(fileFullPath); filePath = Path.Combine("wwwroot", filePath); IFileInfo fileInfo = storageProvider.FileProvider.GetFileInfo(filePath); // // Prepare Default Streaming Requirements ... var mimeType = "application/octetstream"; var readStream = fileInfo.CreateReadStream(); var fileMimeProvider = new FileExtensionContentTypeProvider(); // // Try to Find Physical File's Content Type ... fileMimeProvider.TryGetContentType(filePath, out mimeType); // // Prepare Result Model ... var result = new XFileStreamDescriptorDto { Stream = readStream, MIMEType = mimeType, Name = fileName, Path = fileFullPath }; // return result; } #endregion // #region Referenced Actions ... /// /// Get Reference Identifier for Specified Provider and Specified Item ... /// /// /// /// public string GetIdentifier( string providedFor, TReferenceKey forProvidedId ) { // var result = $"{providedFor}_{forProvidedId}"; // return result; } /// /// Check Specified Item has Refernce to Provider ... /// /// /// /// /// /// public async Task IsProvidedFor( string providedFor, TReferenceKey forProvidedId, Guid id, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = !id.IsNull() && !id.IsDefaultGuid() && !forProvidedId.IsNull() && !providedFor.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Exists ... isValid = await IsExists( id: id, cancellationToken: cancellationToken ); if (!isValid) { XException.NotFound.Throw(); } // var item = await Get( id: id, includeBuilder: null, cancellationToken: cancellationToken ); isValid = !item.IsNullOrDefault(); if (!isValid) { XException.ActionFailed.Throw(); } // var references = item.GetReferences(); var result = references.IsNull() && references.HasChild() && references.Any(r => r.ProvidedFor == providedFor && r.ReferencedTo == $"{forProvidedId}" ); // return result; } /// /// Add Specified Reference to Specified Item ... /// /// /// /// /// /// /// /// /// public async Task AddReference( string providedFor, TReferenceKey forProvidedId, Guid id, int forIndex = 0, XUserClaimsInfoDto userInfo = null, string connectionId = null, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = !providedFor.IsNullOrEmpty() && !id.IsNull() && !id.IsDefaultGuid() && !providedFor.IsNull() && !userInfo.IsNullOrDefault(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Has Reference or not ... isValid = await IsProvidedFor( id: id, providedFor: providedFor, forProvidedId: forProvidedId, cancellationToken: cancellationToken ); if (isValid) { // // Prevent Moving Forward Since Reference Exists ... return; } // // Check Permissions ... var item = await Get( id: id, includeBuilder: null, cancellationToken: cancellationToken ); isValid = !item.IsNullOrDefault() && (IsOwned(item, userInfo) || HasPermision(userInfo)); if (!isValid) { XException.NotAllowed.Throw(); } // var reference = new XReference { Index = forIndex, ProvidedFor = providedFor, ReferencedTo = $"{forProvidedId}" }; var references = item.GetReferences(); references.Add(reference); item = item.UpdateReferences(references); item = await Update( id: id, item: item, userInfo: userInfo, connectionId: connectionId, cancellationToken: cancellationToken ); } /// /// Remove Specified Reference from Specified Item ... /// /// /// /// /// /// /// /// /// public async Task RemoveReference( string providedFor, TReferenceKey forProvidedId, Guid id, int forIndex = 0, XUserClaimsInfoDto userInfo = null, string connectionId = null, CancellationToken cancellationToken = default ) { // // Validate ... var isValid = !id.IsNull() && !id.IsDefaultGuid() && !forProvidedId.IsNull() && !userInfo.IsNullOrDefault() && !providedFor.IsNullOrEmpty(); if (!isValid) { XException.InvalidArgs.Throw(); } // // Check Has Reference or not ... isValid = await IsProvidedFor( id: id, providedFor: providedFor, forProvidedId: forProvidedId, cancellationToken: cancellationToken ); if (!isValid) { // // Prevent Moving Forward Since Reference not Exists ... return; } // // Check Permissions ... var item = await Get( id: id, includeBuilder: null, cancellationToken: cancellationToken ); isValid = !item.IsNullOrDefault() && (IsOwned(item, userInfo) || HasPermision(userInfo)); if (!isValid) { XException.NotAllowed.Throw(); } // var identifier = GetIdentifier( providedFor: providedFor, forProvidedId: forProvidedId ); var references = item.GetReferences(); references = references .Where(r => r.Index != forIndex && r.ProvidedFor != providedFor && r.ReferencedTo != $"{forProvidedId}" ) .ToList(); item = item.UpdateReferences(references); // item = await Update( item: item, id: item.Id, userInfo: userInfo, connectionId: connectionId, cancellationToken: cancellationToken ); } /// /// Get Specified References ... /// /// /// /// /// public async Task>> GetReferences( string providedFor, Guid id, CancellationToken cancellationToken = default ) { // IList> result = new List>(); // var item = await provider.GetAsync( id: id, includeBuilder: null, ignoreSoftDeleteds: true, cancellationToken: cancellationToken ); var isValid = !item.IsNullOrDefault(); if (!isValid) { return result; } // result = item.GetReferences(); // result = result.Where(r => r.ProvidedFor == providedFor ) .ToList(); // return result; } #endregion } }