This commit is contained in:
2026-05-28 19:15:31 +03:30
parent 6101d25a7b
commit b99f0de912
4 changed files with 968 additions and 3 deletions
+213
View File
@@ -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 ...
/// <summary>
/// Stream Specified File ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("{id}/Stream/ById")]
public virtual async Task<ActionResult> 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;
}
}
/// <summary>
/// Stream Specified File ...
/// </summary>
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("{name}/Stream/ByName")]
public virtual async Task<ActionResult> 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;
}
}
/// <summary>
/// Download Specified File ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("{id}/Download/ById")]
public virtual async Task<ActionResult> 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;
}
}
/// <summary>
/// Download Specified File ...
/// </summary>
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("{name}/Download/ByName")]
public virtual async Task<ActionResult> 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;
}
}
/// <summary>
/// Upload Files ...
/// </summary>
/// <param name="files"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost("Upload")]
[RequestSizeLimit(966_367_641)]
public virtual async Task<ActionResult<IEnumerable<XFileDto>>> 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;
}
}
/// <summary>
/// Remove Specified Files ...
/// </summary>
/// <param name="ids"></param>
/// <param name="userInfo"></param>
/// <param name="connectionId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpDelete("")]
public virtual async Task<ActionResult<IEnumerable<Guid>>> 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 ...
/// <summary>
+106 -1
View File
@@ -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<XFile, XFileDto, Guid, XFileDtoHub>, IXBaseReferenced<XFile, Guid>
{ }
{
//
#region Tools ...
/// <summary>
/// Stream Specified File ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<FileStreamResult> Stream(
Guid id,
CancellationToken cancellationToken = default
);
/// <summary>
/// Stream Specified File ...
/// </summary>
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<FileStreamResult> Stream(
string name,
CancellationToken cancellationToken = default
);
/// <summary>
/// Download Specified File ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<PhysicalFileResult> Download(
Guid id,
CancellationToken cancellationToken = default
);
/// <summary>
/// Download Specified File ...
/// </summary>
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<PhysicalFileResult> Download(
string name,
CancellationToken cancellationToken = default
);
/// <summary>
/// Upload Files ...
/// </summary>
/// <param name="files"></param>
/// <param name="userInfo"></param>
/// <param name="connectionId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IEnumerable<XFileDto>> Upload(
IFormFileCollection files,
XUserClaimsInfoDto userInfo = null,
string connectionId = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Remove Specified Files ...
/// </summary>
/// <param name="ids"></param>
/// <param name="userInfo"></param>
/// <param name="connectionId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IEnumerable<Guid>> Remove(
string ids,
XUserClaimsInfoDto userInfo = null,
string connectionId = null,
CancellationToken cancellationToken = default
);
/// <summary>
/// Retrieve Specified File Streaming Info ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XFileStreamDescriptorDto> GetFileDescriptor(
Guid id,
CancellationToken cancellationToken = default
);
/// <summary>
/// Retrieve Specified File Streaming Info ...
/// </summary>
/// <param name="fileName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<XFileStreamDescriptorDto> GetFileDescriptor(
string fileName,
CancellationToken cancellationToken = default
);
#endregion
}
}
+84 -1
View File
@@ -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<XFile, XFileDto, Guid, XFileDtoHub>, IXBaseReferencedController<XFile, Guid>
{ }
{
//
#region Tools ...
/// <summary>
/// Stream Specified File ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("{id}/Stream/ById")]
Task<ActionResult> Stream(
[FromRoute] Guid id,
CancellationToken cancellationToken = default
);
/// <summary>
/// Stream Specified File ...
/// </summary>
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("{name}/Stream/ByName")]
Task<ActionResult> Stream(
[FromRoute] string name,
CancellationToken cancellationToken = default
);
/// <summary>
/// Download Specified File ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("{id}/Download/ById")]
Task<ActionResult> Download(
[FromRoute] Guid id,
CancellationToken cancellationToken = default
);
/// <summary>
/// Download Specified File ...
/// </summary>
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpGet("{name}/Download/ByName")]
Task<ActionResult> Download(
string name,
CancellationToken cancellationToken = default
);
/// <summary>
/// Upload Files ...
/// </summary>
/// <param name="files"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpPost("Upload")]
[RequestSizeLimit(966_367_641)]
Task<ActionResult<IEnumerable<XFileDto>>> Upload(
[FromForm] IFormFileCollection files,
CancellationToken cancellationToken = default
);
/// <summary>
/// Remove Specified Files ...
/// </summary>
/// <param name="ids"></param>
/// <param name="userInfo"></param>
/// <param name="connectionId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
[HttpDelete("")]
Task<ActionResult<IEnumerable<Guid>>> Remove(
[FromQuery] string ids,
CancellationToken cancellationToken = default
);
#endregion
}
}
+565 -1
View File
@@ -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<XFile, XFileDto, Guid, XFileDtoHub>, 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 ...
/// <summary>
/// Stream Specified File ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<FileStreamResult> 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;
}
/// <summary>
/// Stream Specified File ...
/// </summary>
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<FileStreamResult> 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;
}
/// <summary>
/// Download Specified File ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<PhysicalFileResult> 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;
}
/// <summary>
/// Download Specified File ...
/// </summary>
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<PhysicalFileResult> 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;
}
/// <summary>
/// Upload Files ...
/// </summary>
/// <param name="files"></param>
/// <param name="userInfo"></param>
/// <param name="connectionId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IEnumerable<XFileDto>> 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<XFileDto>();
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;
}
/// <summary>
/// Remove Specified Files ...
/// </summary>
/// <param name="ids"></param>
/// <param name="userInfo"></param>
/// <param name="connectionId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IEnumerable<Guid>> 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<Guid>();
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;
}
/// <summary>
/// Retrieve Specified File Streaming Info ...
/// </summary>
/// <param name="id"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<XFileStreamDescriptorDto> 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;
}
/// <summary>
/// Retrieve Specified File Streaming Info ...
/// </summary>
/// <param name="fileName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<XFileStreamDescriptorDto> 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 ...
/// <summary>