diff --git a/Controllers/XFileProviderControllerBase.cs b/Controllers/XFileProviderControllerBase.cs
index d97ab21..37fade7 100644
--- a/Controllers/XFileProviderControllerBase.cs
+++ b/Controllers/XFileProviderControllerBase.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.Logging;
@@ -47,6 +48,218 @@ namespace xFileService.Controllers
this.provider = provider;
}
+ //
+ #region Tools ...
+ ///
+ /// Stream Specified File ...
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{id}/Stream/ById")]
+ public virtual async Task Stream(
+ [FromRoute] Guid id,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ try
+ {
+ //
+ var result = await provider.Stream(
+ id: id,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ return result;
+ }
+ catch (Exception ex)
+ {
+ //
+ var result = GetExceptionActionResult(ex);
+ return result;
+ }
+ }
+
+ ///
+ /// Stream Specified File ...
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{name}/Stream/ByName")]
+ public virtual async Task Stream(
+ [FromRoute] string name,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ try
+ {
+ //
+ var result = await provider.Stream(
+ name: name,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ return result;
+ }
+ catch (Exception ex)
+ {
+ //
+ var result = GetExceptionActionResult(ex);
+ return result;
+ }
+ }
+
+ ///
+ /// Download Specified File ...
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{id}/Download/ById")]
+ public virtual async Task Download(
+ [FromRoute] Guid id,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ try
+ {
+ //
+ var result = await provider.Download(
+ id: id,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ return result;
+ }
+ catch (Exception ex)
+ {
+ //
+ var result = GetExceptionActionResult(ex);
+ return result;
+ }
+ }
+
+ ///
+ /// Download Specified File ...
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{name}/Download/ByName")]
+ public virtual async Task Download(
+ string name,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ try
+ {
+ //
+ var result = await provider.Download(
+ name: name,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ return result;
+ }
+ catch (Exception ex)
+ {
+ //
+ var result = GetExceptionActionResult(ex);
+ return result;
+ }
+ }
+
+ ///
+ /// Upload Files ...
+ ///
+ ///
+ ///
+ ///
+ [HttpPost("Upload")]
+ [RequestSizeLimit(966_367_641)]
+ public virtual async Task>> Upload(
+ [FromForm] IFormFileCollection files,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ try
+ {
+ //
+ // Retrieve User Info ...
+ var userInfo = await GetUserInfo();
+ var connectionId = GetConnectionId();
+
+ //
+ var result = await provider.Upload(
+ files: files,
+ userInfo: userInfo,
+ connectionId: connectionId,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ return Ok(result.ToDynamicObject());
+ }
+ catch (Exception ex)
+ {
+ //
+ var result = GetExceptionActionResult(ex);
+ return result;
+ }
+ }
+
+ ///
+ /// Remove Specified Files ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpDelete("")]
+ public virtual async Task>> Remove(
+ [FromQuery] string ids,
+ CancellationToken cancellationToken = default
+ )
+ {
+ //
+ try
+ {
+ //
+ // Retrieve User Info ...
+ var userInfo = await GetUserInfo();
+ var connectionId = GetConnectionId();
+
+ //
+ var result = await provider.Remove(
+ ids: ids,
+ userInfo: userInfo,
+ connectionId: connectionId,
+ cancellationToken: cancellationToken
+ );
+
+ //
+ return Ok(result.ToDynamicObject());
+ }
+ catch (Exception ex)
+ {
+ //
+ var result = GetExceptionActionResult(ex);
+ return result;
+ }
+ }
+ #endregion
+
//
#region Reference ...
///
diff --git a/Interfaces/IXFileProvider.cs b/Interfaces/IXFileProvider.cs
index 4a9ca48..c931068 100644
--- a/Interfaces/IXFileProvider.cs
+++ b/Interfaces/IXFileProvider.cs
@@ -1,12 +1,117 @@
using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
using xFileService.Models.Dtos;
using xFileService.Models.Entities;
using xFileService.Providers.Dtos;
+using xIdentityModels.Models;
using xIdentityService.Interfaces;
using xTagService.Interfaces;
namespace xFileService.Interfaces
{
public interface IXFileProvider : IXBaseProvidedHasTag, IXBaseReferenced
- { }
+ {
+ //
+ #region Tools ...
+ ///
+ /// Stream Specified File ...
+ ///
+ ///
+ ///
+ ///
+ Task Stream(
+ Guid id,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Stream Specified File ...
+ ///
+ ///
+ ///
+ ///
+ Task Stream(
+ string name,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Download Specified File ...
+ ///
+ ///
+ ///
+ ///
+ Task Download(
+ Guid id,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Download Specified File ...
+ ///
+ ///
+ ///
+ ///
+ Task Download(
+ string name,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Upload Files ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> Upload(
+ IFormFileCollection files,
+ XUserClaimsInfoDto userInfo = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Remove Specified Files ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task> Remove(
+ string ids,
+ XUserClaimsInfoDto userInfo = null,
+ string connectionId = null,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Retrieve Specified File Streaming Info ...
+ ///
+ ///
+ ///
+ ///
+ Task GetFileDescriptor(
+ Guid id,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Retrieve Specified File Streaming Info ...
+ ///
+ ///
+ ///
+ ///
+ Task GetFileDescriptor(
+ string fileName,
+ CancellationToken cancellationToken = default
+ );
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/Interfaces/IXFileProviderController.cs b/Interfaces/IXFileProviderController.cs
index 32a2052..268081e 100644
--- a/Interfaces/IXFileProviderController.cs
+++ b/Interfaces/IXFileProviderController.cs
@@ -1,4 +1,9 @@
using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
using xFileService.Models.Dtos;
using xFileService.Models.Entities;
using xFileService.Providers.Dtos;
@@ -8,5 +13,83 @@ using xTagService.Interfaces;
namespace xFileService.Interfaces
{
public interface IXFileProviderController : IXBaseProvidedHasTagController, IXBaseReferencedController
- { }
+ {
+ //
+ #region Tools ...
+ ///
+ /// Stream Specified File ...
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{id}/Stream/ById")]
+ Task Stream(
+ [FromRoute] Guid id,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Stream Specified File ...
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{name}/Stream/ByName")]
+ Task Stream(
+ [FromRoute] string name,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Download Specified File ...
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{id}/Download/ById")]
+ Task Download(
+ [FromRoute] Guid id,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Download Specified File ...
+ ///
+ ///
+ ///
+ ///
+ [HttpGet("{name}/Download/ByName")]
+ Task Download(
+ string name,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Upload Files ...
+ ///
+ ///
+ ///
+ ///
+ [HttpPost("Upload")]
+ [RequestSizeLimit(966_367_641)]
+ Task>> Upload(
+ [FromForm] IFormFileCollection files,
+ CancellationToken cancellationToken = default
+ );
+
+ ///
+ /// Remove Specified Files ...
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [HttpDelete("")]
+ Task>> Remove(
+ [FromQuery] string ids,
+ CancellationToken cancellationToken = default
+ );
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/Providers/XFileProvider.cs b/Providers/XFileProvider.cs
index f7f9a90..46d89c8 100644
--- a/Providers/XFileProvider.cs
+++ b/Providers/XFileProvider.cs
@@ -1,8 +1,13 @@
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;
@@ -13,6 +18,7 @@ using xFileService.Models.Dtos;
using xFileService.Models.Entities;
using xFileService.Providers.Dtos;
using xIdentityModels.Models;
+using xStorageService.Interfaces;
using xTagService.Providers;
namespace xFileService.Providers
@@ -20,10 +26,12 @@ namespace xFileService.Providers
public class XFileProvider : XBaseProvidedHasTag, IXFileProvider
{
private readonly IXFileServiceProvider provider;
+ private readonly IXStorageProvider storageProvider;
public XFileProvider(
IXFileTagProvider tagProvider,
- IXFileServiceProvider provider
+ IXFileServiceProvider provider,
+ IXStorageProvider storageProvider
) : base(
provider: provider,
tagProvider: tagProvider,
@@ -31,6 +39,7 @@ namespace xFileService.Providers
)
{
this.provider = provider;
+ this.storageProvider = storageProvider;
}
public override bool IsOwned(
@@ -65,6 +74,561 @@ namespace xFileService.Providers
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 ...
///