IFileService, настройки хранилища. EsppDataController
This commit is contained in:
@@ -41,6 +41,12 @@
|
|||||||
public const string getParam = "{id}";
|
public const string getParam = "{id}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class EsppData
|
||||||
|
{
|
||||||
|
public const string UploadTemplates = Base + "/espp-data/templates";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static class Test
|
public static class Test
|
||||||
{
|
{
|
||||||
public const string GetMyIp = Base + "/tests/my-ip";
|
public const string GetMyIp = Base + "/tests/my-ip";
|
||||||
|
|||||||
31
PARR.API/Controllers/V1/EsppDataController.cs
Normal file
31
PARR.API/Controllers/V1/EsppDataController.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using PARR.API.Contracts.V1;
|
||||||
|
using PARR.API.Controllers.V1.Base;
|
||||||
|
using PARR.API.Services.Interfaces;
|
||||||
|
|
||||||
|
namespace PARR.API.Controllers.V1
|
||||||
|
{
|
||||||
|
public class EsppDataController : BaseApiController
|
||||||
|
{
|
||||||
|
private readonly IFileService fileService;
|
||||||
|
|
||||||
|
public EsppDataController(ILogger<EsppDataController> logger, IFileService fileService)
|
||||||
|
{
|
||||||
|
Logger = logger;
|
||||||
|
this.fileService = fileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger<EsppDataController> Logger { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Загрузить файл списка шаблонов полученных из ЕСПП в формате csv
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost(ApiRoutes.EsppData.UploadTemplates)]
|
||||||
|
public IActionResult UploadTemplates([BindRequired] IFormFile file)
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
PARR.API/Domain/UploadResult.cs
Normal file
8
PARR.API/Domain/UploadResult.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace PARR.API.Domain
|
||||||
|
{
|
||||||
|
public class UploadResult
|
||||||
|
{
|
||||||
|
public required string FileName { get; set; }
|
||||||
|
public required string Path { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ namespace PARR.API.Installers
|
|||||||
});
|
});
|
||||||
|
|
||||||
services.AddTransient<IClientService, ClientService>();
|
services.AddTransient<IClientService, ClientService>();
|
||||||
//services.AddTransient<IFileService, FileService>();
|
services.AddTransient<IFileService, FileService>();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace PARR.API.Installers
|
using PARR.API.Settings;
|
||||||
|
|
||||||
|
namespace PARR.API.Installers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Биндинги из конфига appsettings
|
/// Биндинги из конфига appsettings
|
||||||
@@ -7,9 +9,9 @@
|
|||||||
{
|
{
|
||||||
public static void InstallSettings(this IServiceCollection services, IConfiguration configuration)
|
public static void InstallSettings(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
//var storageSettings = new StorageSettings();
|
var storageSettings = new StorageSettings();
|
||||||
//configuration.GetSection(nameof(StorageSettings)).Bind(storageSettings);
|
configuration.GetSection(nameof(StorageSettings)).Bind(storageSettings);
|
||||||
//services.AddSingleton(storageSettings);
|
services.AddSingleton(storageSettings);
|
||||||
|
|
||||||
//TODO: add other
|
//TODO: add other
|
||||||
}
|
}
|
||||||
|
|||||||
101
PARR.API/Services/Implementations/FileService.cs
Normal file
101
PARR.API/Services/Implementations/FileService.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
using PARR.API.Domain;
|
||||||
|
using PARR.API.Services.Interfaces;
|
||||||
|
using PARR.API.Settings;
|
||||||
|
|
||||||
|
namespace PARR.API.Services.Implementations
|
||||||
|
{
|
||||||
|
public class FileService : IFileService
|
||||||
|
{
|
||||||
|
private readonly StorageSettings storageSettings;
|
||||||
|
private readonly ILogger<FileService> logger;
|
||||||
|
|
||||||
|
public FileService(StorageSettings storageSettings, ILogger<FileService> logger)
|
||||||
|
{
|
||||||
|
this.storageSettings = storageSettings;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsExtensionAllowed(IFormFile formFile, string[] allowedExtensions)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(formFile.FileName).ToLower();
|
||||||
|
|
||||||
|
return !string.IsNullOrEmpty(extension) && allowedExtensions.Contains(extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsNotExceededLimit(IFormFile formFile, int limitMb)
|
||||||
|
{
|
||||||
|
var fileSizeByte = formFile.Length;
|
||||||
|
var fileSizeMByte = fileSizeByte / 1024.0 / 1024.0;
|
||||||
|
|
||||||
|
return fileSizeMByte < limitMb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UploadResult?> UploadAsync(IFormFile formFile, string[] rootFolderWithoutStorage)
|
||||||
|
{
|
||||||
|
InitRootFolders(rootFolderWithoutStorage);
|
||||||
|
|
||||||
|
//Генерим уникальное имя файла: старое имя + рандом в середину
|
||||||
|
var newFileName = $"{Path.GetFileNameWithoutExtension(formFile.FileName)}-{Path.GetFileNameWithoutExtension(Path.GetRandomFileName())}{Path.GetExtension(formFile.FileName)}";
|
||||||
|
|
||||||
|
|
||||||
|
var relativePath = Path.Combine(Path.Combine(rootFolderWithoutStorage), newFileName);
|
||||||
|
var absPath = Path.Combine(storageSettings.StoragePath, relativePath);
|
||||||
|
|
||||||
|
if (File.Exists(absPath))
|
||||||
|
{
|
||||||
|
logger.LogError($"Не могу сохранить файл, такой файл уже существует: {absPath}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var origFileName = Path.GetFileName(formFile.FileName);
|
||||||
|
|
||||||
|
using (var stream = new FileStream(absPath, FileMode.Create))
|
||||||
|
{
|
||||||
|
await formFile.CopyToAsync(stream);
|
||||||
|
logger.LogInformation($"Файл сохранен {origFileName}, {absPath}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UploadResult { FileName = origFileName, Path = relativePath };
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogError(ex, $"Ошибка при сохранении файла {absPath}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitRootFolders(string[] rootFolderWithoutStorage)
|
||||||
|
{
|
||||||
|
if (!rootFolderWithoutStorage.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var path = string.Empty;
|
||||||
|
foreach (var item in rootFolderWithoutStorage)
|
||||||
|
{
|
||||||
|
path = Path.Combine(path, item);
|
||||||
|
CreateDirectory(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDirectory(string relativePath)
|
||||||
|
{
|
||||||
|
var absolutePath = Path.Combine(storageSettings.StoragePath, relativePath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(absolutePath))
|
||||||
|
Directory.CreateDirectory(absolutePath);
|
||||||
|
|
||||||
|
// не нужно ничего возвращать, при сохраненнии файла будет ошибка и ее обработаем
|
||||||
|
//return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.LogError(e, $"Не могу создать директорию: {absolutePath}");
|
||||||
|
//return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
PARR.API/Services/Interfaces/IFileService.cs
Normal file
25
PARR.API/Services/Interfaces/IFileService.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using PARR.API.Domain;
|
||||||
|
|
||||||
|
namespace PARR.API.Services.Interfaces
|
||||||
|
{
|
||||||
|
public interface IFileService
|
||||||
|
{
|
||||||
|
Task<UploadResult?> UploadAsync(IFormFile formFile, string[] rootFolderWithoutStorage);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Размер файла не превышает лимит?
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="formFile"></param>
|
||||||
|
/// <param name="limit"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool IsNotExceededLimit(IFormFile formFile, int limitMb);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Разрешено это расширение?
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="formFile"></param>
|
||||||
|
/// <param name="allowedExtensions"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool IsExtensionAllowed(IFormFile formFile, string[] allowedExtensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
24
PARR.API/Settings/StorageSettings.cs
Normal file
24
PARR.API/Settings/StorageSettings.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace PARR.API.Settings
|
||||||
|
{
|
||||||
|
public class StorageSettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Путь к хранилищу
|
||||||
|
/// </summary>
|
||||||
|
public string StoragePath { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Настройки хранилища загрузки шаблонов ЕСПП
|
||||||
|
/// </summary>
|
||||||
|
public StorageSettingsEsspTemplates? EsppTemplates { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StorageSettingsEsspTemplates
|
||||||
|
{
|
||||||
|
public string[] TemplatePath => new string[] { "espp-templates" };
|
||||||
|
|
||||||
|
public string[] AllowedExtensions { get; set; } = Array.Empty<string>();
|
||||||
|
|
||||||
|
public int MaxFileSizeMb { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,5 +4,8 @@
|
|||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"StorageSettings": {
|
||||||
|
"StoragePath": "W:\\"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,5 +29,12 @@
|
|||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
"CorsSettings": {
|
"CorsSettings": {
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
|
},
|
||||||
|
"StorageSettings": {
|
||||||
|
"StoragePath": "Data",
|
||||||
|
"EsppTemplates": {
|
||||||
|
"AllowedExtensions": [ ".csv" ],
|
||||||
|
"MaxFileSizeMb": 100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user