using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.FileProviders; using xCommons.Constants; using xCommons.Extensions; using xExceptions.Constants; using xModels.Dtos; using xStorageService.Constants; using xStorageService.Helpers; using xStorageService.Interfaces; using xStorageService.Models; namespace xStorageService.Providers { public partial class XStorageProvider : IXStorageProvider { public readonly string BasePath; private readonly XFileType[] ThumbSupprtedTypes = new XFileType[] { XFileType.Image, XFileType.CoverImage, XFileType.ProfileImasge }; public IFileProvider FileProvider { get; } public XStorageHelper StorageHelper { get; } public IXThumbnailProvider ThumbnailProvider { get; } public XStorageConfiguration Configuration { get; } public XStorageProvider( IFileProvider fileProvider, XStorageHelper storageHelper, IXThumbnailProvider thumbnailProvider, XStorageConfiguration configuration ) { // FileProvider = fileProvider; Configuration = configuration; StorageHelper = storageHelper; ThumbnailProvider = thumbnailProvider; // BasePath = Path .Combine(Directory .GetCurrentDirectory(), Configuration.BaseFolder); } // #region Special Folders Path Getter (s) ... /// /// Get Storage Folder Path /// /// public string GetStorageFolderPath() { return Path.Combine(BasePath, Configuration.Storage); } /// /// Get Temp Folder Path /// /// public string GetTempFolderPath() { return Path.Combine(GetStorageFolderPath(), Configuration.Temp); } /// /// Get Uploads Folder Path /// /// public string GetUploadsFolderPath() { return Path.Combine(GetStorageFolderPath(), Configuration.Uploads); } /// /// Get Widgets Folder Path /// /// public string GetWidgetsFolderPath() { return Path.Combine(GetStorageFolderPath(), Configuration.Widgets); } /// /// Get Thumb Folder Path /// /// public string GetThumbFolderPath() { return Path.Combine(GetUploadsFolderPath(), Configuration.Thumb); } /// /// Get Images Folder Path /// /// public string GetImageFolderPath() { return Path.Combine(GetUploadsFolderPath(), Configuration.Image); } /// /// Get Audios Folder Path /// /// public string GetAudioFolderPath() { return Path.Combine(GetUploadsFolderPath(), Configuration.Audio); } /// /// Get Videos Folder Path /// /// public string GetVideoFolderPath() { return Path.Combine(GetUploadsFolderPath(), Configuration.Video); } /// /// Get Documents Folder Path /// /// public string GetDocumentFolderPath() { return Path.Combine(GetUploadsFolderPath(), Configuration.Document); } /// /// Prepare Full Path to Use for Static Files /// /// /// public string FilePathResolver(string fullPath) { return fullPath.Replace(BasePath, ""); } /// /// Convert a File Path to Url /// /// /// public string ConvertToProfileImageUrl(string fullPath) { // var authority = !Configuration.IdentityAuthority.IsNullOrEmpty() && Configuration.IdentityAuthority.EndsWith("/") ? Configuration.IdentityAuthority.Substring(0, Configuration.IdentityAuthority.Length - 1) : Configuration.IdentityAuthority; // return $"{authority}{FilePathResolver(fullPath)}"; } /// /// Convert a File Path to Url /// /// /// public string ConvertToFileUrl(string fullPath) { // var authority = !Configuration.Authority.IsNullOrEmpty() && Configuration.Authority.EndsWith("/") ? Configuration.Authority.Substring(0, Configuration.Authority.Length - 1) : Configuration.Authority; // return $"{authority}/{FilePathResolver(fullPath)}"; } #endregion // #region Prepare Storage Structure ... /// /// Check Folder Structure of Storage Area /// and Create them if not Exists /// public void Prepare() { // try { // // Storage Folder Path Check ... if (!IsFolderExists(GetStorageFolderPath())) { Directory.CreateDirectory(GetStorageFolderPath()); } // // Temp Folder Path Check ... if (!IsFolderExists(GetTempFolderPath())) { Directory.CreateDirectory(GetTempFolderPath()); } // // Temp Folder Path Check ... if (!IsFolderExists(GetThumbFolderPath())) { Directory.CreateDirectory(GetThumbFolderPath()); } // // Widgets Folder Path Chekc ... if (!IsFolderExists(GetWidgetsFolderPath())) { Directory.CreateDirectory(GetWidgetsFolderPath()); } // // Uploads Folder Path Chekc ... if (!IsFolderExists(GetUploadsFolderPath())) { Directory.CreateDirectory(GetUploadsFolderPath()); } // // Image Folder Path Chekc ... if (!IsFolderExists(GetImageFolderPath())) { Directory.CreateDirectory(GetImageFolderPath()); } // // Music Folder Path Chekc ... if (!IsFolderExists(GetAudioFolderPath())) { Directory.CreateDirectory(GetAudioFolderPath()); } // // Video Folder Path Chekc ... if (!IsFolderExists(GetVideoFolderPath())) { Directory.CreateDirectory(GetVideoFolderPath()); } // // Document Folder Path Chekc ... if (!IsFolderExists(GetDocumentFolderPath())) { Directory.CreateDirectory(GetDocumentFolderPath()); } } catch { XException.StorageServiceInitialFailed.Throw(); } // // Set Temp Folder for Actions ... ThumbnailProvider.SetTempFolder(GetTempFolderPath()); } #endregion // #region Validators ... /// /// Detrmines a Path Exists or not /// /// /// public bool IsFolderExists(string path) { return Directory.Exists(path); } /// /// Check a File Exists or not /// /// /// public bool IsFileExists(string filePath) { return File.Exists(filePath); } /// /// Check a File Name Support Thumbnail or not /// /// /// public bool IsSupportThumb(string fileName) { // if (!fileName.StartsWith(Configuration.FilePrefix)) { XException.InavlidFile.Throw(); } // var fileType = StorageHelper.GetType(fileName); return IsSupportThumb(fileType); } /// /// Check a FileType Support Thumbnail or not /// /// /// public bool IsSupportThumb(XFileType type) { return ThumbSupprtedTypes.Contains(type); } /// /// Validate a File Exists or not /// /// public void ValidateFileExists(string filePath) { // if (!IsFileExists(filePath)) { XException.NotFound.Throw(); } } /// /// Validate a File Model with it's Related Physical File /// /// public void ValidateFileModel(XFileDto file) { // // Validate Args ... if (file == null) { XException.InvalidArgs.Throw(); } // // Retrieve Full File Path ... var fileFullPath = file.Path; if (fileFullPath.IsNullOrEmpty()) { XException.InavlidFile.Throw(); } // // Retrieve is File Exists or not ... var isPhysicalFileExists = IsFileExists(file.Path); if (!isPhysicalFileExists) { XException.NotFound.Throw(); } } /// /// Validate File for Upload /// /// public void ValidateFile(IFormFile file) { // // Validate Args ... if (file == null) { XException.InvalidArgs.Throw(); } // // Check File Size not Zero ... if (file.Length == 0) { XException.EmptyFile.Throw(); } // // Check File Max Size ... if (file.Length > Configuration.MaxFileSize) { XException.MaxFileSizeExceeded.Throw(); } } /// /// Validate a Collection of IFormFile /// /// public void ValidateFiles(IFormFileCollection files) { // // Validate Args ... if (!files.HasChild()) { XException.InvalidArgs.Throw(); } // files.ToList() .ForEach(f => ValidateFile(f)); } /// /// Validate File for Profile Image /// /// public void ValidateProfileImage(IFormFile file) { // // Check File Validation ... // since Args Checked in File Validation, there is no need to // double check it ... ValidateFile(file); // // Check File Type ... var fileType = GetFileType(file.FileName); if (fileType != XFileType.Image) { XException.UnsupportedFileType.Throw(); } } #endregion // #region Generators ... /// /// Generate New File /// /// public string NewFileName() { return Configuration.FilePrefix + Guid.NewGuid().ToString().ToLower(); } /// /// Retrieve Related Thumnail File /// /// /// public string GenerateThumbName(string fileName) { // if (!IsSupportThumb(fileName)) { XException.UnsupportedFileType.Throw(); } // return fileName.Replace(Configuration.FilePrefix, Configuration.ThumbPrefix); } /// /// Get a New FileName with Extension based on Given fileName /// /// /// public string GetNewFileName(string fileName) { // // Validate Args ... if (fileName.IsNullOrEmpty()) { XException.InvalidArgs.Throw(); } // var fileExt = GetFileExtension(fileName); var newFileNameWithoutExt = NewFileName(); // // Generate New File Name ... var newFileName = string.Format("{0}{1}", newFileNameWithoutExt, fileExt); // return newFileName; } #endregion // #region Tools Actions ... /// /// Extract a FileName's Extension /// /// /// public string GetFileExtension(string fileName) { // var result = StorageHelper.GetFileExtension(fileName); if (result.IsNullOrEmpty()) { XException.InvalidArgs.Throw(); } // return result; } /// /// Extract a FileName's Without it's Extension /// /// /// public string GetFileNameWithoutExtension(string fileName) { // var result = StorageHelper.GetFileNameWithoutExtension(fileName); if (result.IsNullOrEmpty()) { XException.InvalidArgs.Throw(); } // return result; } /// /// Get file Extension of Specified File Name /// /// /// public FileExtensions GetFileExtensionType(string fileName) { // var fileExt = GetFileExtension(fileName); if (fileExt.IsNullOrEmpty()) { XException.InvalidData.Throw(); } // var result = StorageHelper.GetExtensionType(fileExt); if (result == -1) { XException.UnsupportedFileType.Throw(); } // return (FileExtensions)result; } /// /// Return File Type of Specific File Name /// /// /// public XFileType GetFileType(string fileName) { // var fileExtension = GetFileExtensionType(fileName); return GetFileType(fileExtension); } /// /// Return File Type of Specific Extension /// /// /// public XFileType GetFileType(FileExtensions fileExtension) { return StorageHelper.GetType((int)fileExtension); } /// /// Retrieve Full Path of thumbnail File /// /// /// public string GetThumbnailFileFullPath(string thumFileName) { // if (!thumFileName.StartsWith(Configuration.ThumbPrefix)) { XException.InavlidFile.Throw(); } // return Path.Combine(GetThumbFolderPath(), thumFileName); } /// /// Retrieve a File Thumbnail Path if Supported /// /// /// public string GetRelatedThumbnailFullPath(string fileName) { // var thumbName = GenerateThumbName(fileName); return GetThumbnailFileFullPath(thumbName); } /// /// Retrieve Special File Type's Folder Path /// /// /// public string GetFileTypeFolderPath(XFileType type) { // // Retrieve Folder Path Based on file ... switch (type) { case XFileType.Image: case XFileType.CoverImage: case XFileType.ProfileImasge: return GetImageFolderPath(); case XFileType.Audio: return GetAudioFolderPath(); case XFileType.Video: return GetVideoFolderPath(); case XFileType.Document: default: return GetDocumentFolderPath(); } } /// /// Retrieve a File's Folder path /// /// /// /// /// public string GetFileFolderPath( string fileName, bool isTemp = false, bool isWidget = false ) { // // Validate Args ... if (fileName.IsNullOrEmpty()) { XException.InvalidArgs.Throw(); } // var fileType = GetFileType(fileName); // var folderPath = ""; // // if isTemp equals true // ignore file type and return Temp Folder ... if (isTemp) { folderPath = GetTempFolderPath(); } // // if isWidget equals true and isTemp equals false // ignore file type and return Widget's Folder ... if (isWidget && !isTemp) { folderPath = GetWidgetsFolderPath(); } // // if both isTemp and isWidget equals false // which is the default state, retrieve proper folder path // based on file type and return it ... if (!isTemp && !isWidget) { folderPath = GetFileTypeFolderPath(fileType); } // // Check folderPath restrict to have Value ... if (folderPath.IsNullOrEmpty()) { XException.InvalidData.Throw(); } // // return result ... return folderPath; } /// /// Retrieve a Full path of a File /// /// /// /// /// public string GetFileFullPath( string fileName, bool isTemp = false, bool isWidget = false ) { // var folderPath = GetFileFolderPath( fileName, isTemp: isTemp, isWidget: isWidget ); // if (folderPath.IsNullOrEmpty()) { XException.ActionFailed.Throw(); } // return Path.Combine(folderPath, fileName); } /// /// Save a File into Storage Based on it's Type /// /// /// /// /// /// public async Task SaveFileAsync( IFormFile file, bool isTemp = false, bool isWidget = false, CancellationToken cancellationToken = default ) { // // Validate Args ... ValidateFile(file); // // Extract and Generate required Data ... var fileName = GetNewFileName(file.FileName); var folderPath = GetFileFolderPath(file.FileName, isTemp, isWidget); var fileFullPath = Path.Combine(folderPath, fileName); // // Save File ... using (var stream = new FileStream(fileFullPath, FileMode.Create)) { // await file.CopyToAsync( stream, cancellationToken ); } // // Since all Required data can be Generated based on // a simple file Name, there is no need to return additional // data as result ... return fileName; } /// /// Delete a Physical File by Validate it's Exists /// /// /// /// public void DeleteFile( string fileFullPath, bool forceFileExists = true, Exception exception = null ) { // // Validate Args ... if (!forceFileExists && fileFullPath.IsNullOrEmpty()) { XException.InvalidArgs.Throw(); } // // Prepare Default Exception ... if (exception == null) { exception = XException.NotFound.ToException(); } // // Check File Exists ... var isFileExists = File.Exists(fileFullPath); if (forceFileExists && !isFileExists) { XException.NotFound.Throw(); } // // Handle Prevent Error when forceFileExists false // and File doesn't Exists ... if (!forceFileExists && !isFileExists) { return; } // File.Delete(fileFullPath); } /// /// Remove a List of Physical Files Path /// and return Un Removable Files ... /// /// /// /// /// public ICollection DeleteFiles( ICollection fileFullPaths, bool forceFileExists = true, Exception exception = null ) { // if (exception.IsNull()) { exception = XException.NotFound.ToException(); } // var result = fileFullPaths.Where(f => !IsFileExists(GetFileFullPath(f))).ToList(); if (forceFileExists && result.HasChild()) { throw exception; } // var existsFile = fileFullPaths.Where(f => IsFileExists(f)).ToList(); existsFile.ForEach(f => { DeleteFile( f, forceFileExists: forceFileExists, exception: exception ); }); // return fileFullPaths.Except(existsFile).ToList(); } /// /// Create Thumbnail based on FileName.Ext /// /// /// /// public async Task CreateThumbnail( string fileName, CancellationToken cancellationToken = default ) { // if (fileName.IsNullOrEmpty()) { return; } // await Task.Run(() => { // var fileFullPath = GetFileFullPath(fileName); if (!IsFileExists(fileFullPath)) { XException.NotFound.Throw(); } // var thumbFileName = GenerateThumbName(fileName); var thumbFullPath = GetThumbnailFileFullPath(thumbFileName); // ThumbnailProvider.ResizeImage( fileFullPath, thumbFullPath, Configuration.ThumbSize, Configuration.ThumbQuality ); }, cancellationToken); } /// /// Create Thumbnail based on XFile model /// /// /// /// public async Task CreateThumbnail( XFileDto file, CancellationToken cancellationToken = default ) { // await CreateThumbnail( file.Name, cancellationToken ); } /// /// Save and Create a Thumbnail For Profile Image /// /// /// /// public async Task HandleProfileImageSave( IFormFile file, CancellationToken cancellationToken = default ) { // ValidateProfileImage(file); // var savedFileName = await SaveFileAsync( file, isTemp: false, isWidget: false, cancellationToken: cancellationToken ); var thumbFileName = GenerateThumbName(savedFileName); // var savedFileFullPath = GetFileFullPath(savedFileName); var thumbFullPath = GetThumbnailFileFullPath(thumbFileName); // await CreateThumbnail( savedFileName, cancellationToken ); // return new XFileResult { Name = file.Name, FileName = savedFileName, FilePath = ConvertToProfileImageUrl(savedFileFullPath), Thmbnail = thumbFileName, ThmbnailPath = ConvertToProfileImageUrl(thumbFullPath) }; } /// /// Handle File Upload with Support of Thumbnails ... /// /// /// /// public async Task HandleFileSave( IFormFile file, CancellationToken cancellationToken = default ) { // ValidateFile(file); // var savedFileName = await SaveFileAsync(file, isTemp: false, isWidget: false); // var savedFileFullPath = GetFileFullPath(savedFileName); // var thumbFileName = ""; var thumbFullPath = ""; var fileType = GetFileType(savedFileName); if (fileType.IsImageKind()) { // thumbFileName = GenerateThumbName(savedFileName); thumbFullPath = GetThumbnailFileFullPath(thumbFileName); // await CreateThumbnail(savedFileName); } // var result = new XFileResult { Name = file.FileName, FileName = savedFileName, FilePath = ConvertToFileUrl(savedFileFullPath), Thmbnail = thumbFileName, ThmbnailPath = ConvertToFileUrl(thumbFullPath), }; // return result; } /// /// Handle File Upload with Support of Thumbnails ... /// /// /// /// public async Task> HandleFilesSave( IFormFileCollection files, CancellationToken cancellationToken = default ) { // ValidateFiles(files); // var result = new Collection(); foreach (var file in files) { // var savedFileName = await SaveFileAsync( file, isTemp: false, isWidget: false, cancellationToken: cancellationToken ); // var savedFileFullPath = GetFileFullPath(savedFileName); // var thumbFileName = ""; var thumbFullPath = ""; var fileType = GetFileType(savedFileName); if (fileType.IsImageKind()) { // thumbFileName = GenerateThumbName(savedFileName); thumbFullPath = GetThumbnailFileFullPath(thumbFileName); // await CreateThumbnail( savedFileName, cancellationToken ); } // result.Add( new XFileResult { Name = file.FileName, FileName = savedFileName, FilePath = ConvertToFileUrl(savedFileFullPath), Thmbnail = thumbFileName, ThmbnailPath = ConvertToFileUrl(thumbFullPath), }); } // return result; } #endregion } }