IFileService, настройки хранилища. EsppDataController

This commit is contained in:
Mikhail Trubnikov
2023-08-24 17:03:12 +10:00
parent f3914c07a4
commit 49ba6569b1
10 changed files with 212 additions and 5 deletions

View File

@@ -41,6 +41,12 @@
public const string getParam = "{id}";
}
public static class EsppData
{
public const string UploadTemplates = Base + "/espp-data/templates";
}
public static class Test
{
public const string GetMyIp = Base + "/tests/my-ip";

View 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();
}
}
}

View File

@@ -0,0 +1,8 @@
namespace PARR.API.Domain
{
public class UploadResult
{
public required string FileName { get; set; }
public required string Path { get; set; }
}
}

View File

@@ -20,7 +20,7 @@ namespace PARR.API.Installers
});
services.AddTransient<IClientService, ClientService>();
//services.AddTransient<IFileService, FileService>();
services.AddTransient<IFileService, FileService>();
}

View File

@@ -1,4 +1,6 @@
namespace PARR.API.Installers
using PARR.API.Settings;
namespace PARR.API.Installers
{
/// <summary>
/// Биндинги из конфига appsettings
@@ -7,9 +9,9 @@
{
public static void InstallSettings(this IServiceCollection services, IConfiguration configuration)
{
//var storageSettings = new StorageSettings();
//configuration.GetSection(nameof(StorageSettings)).Bind(storageSettings);
//services.AddSingleton(storageSettings);
var storageSettings = new StorageSettings();
configuration.GetSection(nameof(StorageSettings)).Bind(storageSettings);
services.AddSingleton(storageSettings);
//TODO: add other
}

View 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;
}
}
}
}

View 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);
}
}

View 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; }
}
}

View File

@@ -4,5 +4,8 @@
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"StorageSettings": {
"StoragePath": "W:\\"
}
}

View File

@@ -29,5 +29,12 @@
"AllowedHosts": "*",
"CorsSettings": {
"AllowedHosts": "*"
},
"StorageSettings": {
"StoragePath": "Data",
"EsppTemplates": {
"AllowedExtensions": [ ".csv" ],
"MaxFileSizeMb": 100
}
}
}