446 lines
18 KiB
C#
446 lines
18 KiB
C#
using Core;
|
|
using ICSharpCode.SharpZipLib.Core;
|
|
using ICSharpCode.SharpZipLib.Zip;
|
|
using LiMan.APi.Data;
|
|
using LiMan.DB.DBModels;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.StaticFiles;
|
|
using Microsoft.Extensions.Configuration;
|
|
using NLog;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace LiMan.APi.Controllers
|
|
{
|
|
/// <summary>
|
|
/// Controller caricamento file
|
|
/// </summary>
|
|
[ApiController]
|
|
[Route("api/filesave")]
|
|
public class FilesaveController : ControllerBase
|
|
{
|
|
#region Public Constructors
|
|
|
|
/// <summary>
|
|
/// Init generico
|
|
/// </summary>
|
|
/// <param name="configuration"></param>
|
|
/// <param name="DataService"></param>
|
|
/// <param name="env"></param>
|
|
public FilesaveController(IConfiguration configuration, ApiDataService DataService, IWebHostEnvironment env)
|
|
{
|
|
dataService = DataService;
|
|
_configuration = configuration;
|
|
this.env = env;
|
|
Log.Info("Avviata classe FilesaveController");
|
|
}
|
|
|
|
#endregion Public Constructors
|
|
|
|
#region Public Methods
|
|
|
|
/// GET api/filesave/id/filename
|
|
/// <summary>
|
|
/// Recupera un singolo file dato ticket code + nome file (safe)
|
|
/// </summary>
|
|
/// <param name="id">Ticket code formato T00000000</param>
|
|
/// <param name="secureName">Nome file (safe)</param>
|
|
/// <param name="fileName">Nome file da scaricare</param>
|
|
/// <returns></returns>
|
|
[HttpGet("{id}/{secureName}/{fileName}")]
|
|
public async Task<ActionResult> DownloadFile(string id, string secureName, string fileName)
|
|
{
|
|
string relDir = _configuration["ServerConf:FileShareTickets"];
|
|
string ticketDir = Path.Combine(relDir, id);
|
|
var filePath = Path.Combine(ticketDir, secureName);
|
|
// verifico esistenza..
|
|
if (System.IO.File.Exists(filePath))
|
|
{
|
|
var bytes = await System.IO.File.ReadAllBytesAsync(filePath);
|
|
return File(bytes, GetMimeType(fileName), fileName);
|
|
}
|
|
else
|
|
{
|
|
return File(new byte[0], "text/plain", "Empty.txt");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Elenco files associati a ticket supporto POST api/filesave/list/1
|
|
/// </summary>
|
|
/// <param name="id"></param>
|
|
/// <param name="CurrRequest"></param>
|
|
/// <returns></returns>
|
|
[HttpPost("list/{id}")]
|
|
public async Task<List<FileAttachModel>> list(int id, [FromBody] SupportRequest CurrRequest)
|
|
{
|
|
List<FileAttachModel> result = new List<FileAttachModel>();
|
|
// controllo valori
|
|
if (CurrRequest.IsValid)
|
|
{
|
|
// cerco i files dato ticket
|
|
result = await dataService.FileGetFilt(id);
|
|
await dataService.recordCall(CurrRequest.CodInst, CurrRequest.CodApp, $"POST:api/files/list:{id}");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Caricamento file effettivo via POST
|
|
/// </summary>
|
|
/// <param name="ticketId">TicketId x riferimento</param>
|
|
/// <param name="files">Elenco files da caricare</param>
|
|
/// <returns></returns>
|
|
[HttpPost()]
|
|
public async Task<ActionResult<IList<UploadResult>>> PostFiles([FromForm] int ticketId, [FromForm] IEnumerable<IFormFile> files)
|
|
{
|
|
// max 10 files
|
|
var maxAllowedFiles = 10;
|
|
// max 50 mb
|
|
long maxFileSize = 1024 * 1024 * 50;
|
|
var filesProcessed = 0;
|
|
string ticketDir = $"T{ticketId:000000000}";
|
|
var resourcePath = new Uri($"{Request.Scheme}://{Request.Host}/api/filesave/list/{ticketId}");
|
|
List<UploadResult> uploadResults = new();
|
|
string fileDir = env.ContentRootPath;
|
|
string relDir = env.EnvironmentName;
|
|
|
|
foreach (var file in files)
|
|
{
|
|
var uploadResult = new UploadResult();
|
|
string trustedFileNameForFileStorage;
|
|
var untrustedFileName = file.FileName;
|
|
uploadResult.FileName = untrustedFileName;
|
|
var trustedFileNameForDisplay = WebUtility.HtmlEncode(untrustedFileName);
|
|
|
|
if (filesProcessed < maxAllowedFiles)
|
|
{
|
|
if (file.Length == 0)
|
|
{
|
|
Log.Info($"{trustedFileNameForDisplay} length is 0 (Err: 1)");
|
|
uploadResult.ErrorCode = 1;
|
|
}
|
|
else if (file.Length > maxFileSize)
|
|
{
|
|
Log.Info($"{trustedFileNameForDisplay} of {CalcSize(file.Length)} is larger than the limit of {CalcSize(maxFileSize)} (Err: 2)");
|
|
uploadResult.ErrorCode = 2;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
DateTime oggi = DateTime.Today;
|
|
trustedFileNameForFileStorage = Path.GetRandomFileName();
|
|
relDir = _configuration["ServerConf:FileShareTickets"];
|
|
fileDir = Path.Combine(relDir, ticketDir);
|
|
if (!Directory.Exists(fileDir))
|
|
{
|
|
Directory.CreateDirectory(fileDir);
|
|
}
|
|
var path = Path.Combine(fileDir, trustedFileNameForFileStorage);
|
|
|
|
await using FileStream fs = new(path, FileMode.Create);
|
|
await file.CopyToAsync(fs);
|
|
|
|
Log.Info($"{trustedFileNameForDisplay} saved at {path}");
|
|
uploadResult.Uploaded = true;
|
|
uploadResult.StoredFileName = trustedFileNameForFileStorage;
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
Log.Error($"{trustedFileNameForDisplay} error on upload (Err: 3): {ex.Message}");
|
|
uploadResult.ErrorCode = 3;
|
|
}
|
|
}
|
|
|
|
filesProcessed++;
|
|
}
|
|
else
|
|
{
|
|
Log.Info($"{trustedFileNameForDisplay} not uploaded because the request exceeded the allowed {maxAllowedFiles} of files (Err: 4)");
|
|
uploadResult.ErrorCode = 4;
|
|
}
|
|
|
|
uploadResults.Add(uploadResult);
|
|
}
|
|
// salvo su DB
|
|
var fatto = dataService.FileAdd(ticketId, ticketDir, uploadResults);
|
|
Log.Info($"Ticket: {ticketId} | dir: {ticketDir} | {uploadResults.Count} files");
|
|
|
|
return new CreatedResult(resourcePath, uploadResults);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Caricamento file effettivo via POST
|
|
/// </summary>
|
|
/// <param name="ticketId">TicketId x riferimento</param>
|
|
/// <param name="file">Elenco files da caricare</param>
|
|
/// <returns></returns>
|
|
[HttpPost("single")]
|
|
public async Task<ActionResult<UploadResult>> PostSingleFile([FromForm] int ticketId, [FromForm] IFormFile file)
|
|
{
|
|
// max 200 mb
|
|
long maxFileSize = 1024 * 1024 * 200;
|
|
string ticketDir = $"T{ticketId:000000000}";
|
|
var resourcePath = new Uri($"{Request.Scheme}://{Request.Host}/api/filesave/list/{ticketId}");
|
|
List<UploadResult> uploadResults = new();
|
|
string fileDir = env.ContentRootPath;
|
|
string relDir = env.EnvironmentName;
|
|
|
|
var uploadResult = new UploadResult();
|
|
string trustedFileNameForFileStorage;
|
|
var untrustedFileName = file.FileName;
|
|
uploadResult.FileName = untrustedFileName;
|
|
var trustedFileNameForDisplay = WebUtility.HtmlEncode(untrustedFileName);
|
|
|
|
if (file.Length == 0)
|
|
{
|
|
Log.Info($"{trustedFileNameForDisplay} length is 0 (Err: 1)");
|
|
uploadResult.ErrorCode = 1;
|
|
}
|
|
else if (file.Length > maxFileSize)
|
|
{
|
|
Log.Info($"{trustedFileNameForDisplay} of {CalcSize(file.Length)} is larger than the limit of {CalcSize(maxFileSize)} (Err: 2)");
|
|
uploadResult.ErrorCode = 2;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
DateTime oggi = DateTime.Today;
|
|
trustedFileNameForFileStorage = Path.GetRandomFileName();
|
|
relDir = _configuration["ServerConf:FileShareTickets"];
|
|
fileDir = Path.Combine(relDir, ticketDir);
|
|
if (!Directory.Exists(fileDir))
|
|
{
|
|
Directory.CreateDirectory(fileDir);
|
|
}
|
|
var path = Path.Combine(fileDir, trustedFileNameForFileStorage);
|
|
|
|
await using FileStream fs = new(path, FileMode.Create);
|
|
await file.CopyToAsync(fs);
|
|
|
|
Log.Info($"{trustedFileNameForDisplay} saved at {path}");
|
|
uploadResult.Uploaded = true;
|
|
uploadResult.StoredFileName = trustedFileNameForFileStorage;
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
Log.Error($"{trustedFileNameForDisplay} error on upload (Err: 3): {ex.Message}");
|
|
uploadResult.ErrorCode = 3;
|
|
}
|
|
}
|
|
|
|
uploadResults.Add(uploadResult);
|
|
// salvo su DB
|
|
var fatto = dataService.FileAdd(ticketId, ticketDir, uploadResults);
|
|
Log.Info($"Ticket: {ticketId} | dir: {ticketDir} | {uploadResults.Count} files");
|
|
|
|
return new CreatedResult(resourcePath, uploadResult);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Caricamento file backup applicativo in formato ZIP via FORM POST (backup configurazione applicativo, file protetto da masterKey)
|
|
/// </summary>
|
|
/// <param name="CodApp">Applicazione di riferimento</param>
|
|
/// <param name="CodInst">Installazione di riferimento</param>
|
|
/// <param name="DoUnzip">Richiesta UnZip file post caricamento</param>
|
|
/// <param name="ForceApprov">
|
|
/// Richiesta di approvazione salvataggio files modificati post upload + unzip
|
|
/// </param>
|
|
/// <param name="ZipFile">File da caricare ed estrarre</param>
|
|
/// <returns></returns>
|
|
[HttpPost("zipbackup")]
|
|
public async Task<ActionResult<UploadResult>> PostZipFile([FromForm] string CodApp, [FromForm] string CodInst, [FromForm] bool DoUnzip, [FromForm] bool ForceApprov, [FromForm] IFormFile ZipFile)
|
|
{
|
|
// max 200 mb
|
|
long maxFileSize = 1024 * 1024 * 200;
|
|
// preparo oggetti x risposta
|
|
var resourcePath = new Uri($"{Request.Scheme}://{Request.Host}/api/filesave/");
|
|
List<UploadResult> uploadResults = new List<UploadResult>();
|
|
string fileDir = env.ContentRootPath;
|
|
string relDir = env.EnvironmentName;
|
|
string authKey = "";
|
|
var uploadResult = new UploadResult();
|
|
uploadResult.FileName = ZipFile.FileName;
|
|
// controllo size e procedo
|
|
if (ZipFile.Length == 0)
|
|
{
|
|
Log.Info($"{ZipFile.FileName} length is 0 (Err: 1)");
|
|
uploadResult.ErrorCode = 1;
|
|
}
|
|
else if (ZipFile.Length > maxFileSize)
|
|
{
|
|
Log.Info($"{ZipFile.FileName} of {CalcSize(ZipFile.Length)} is larger than the limit of {CalcSize(maxFileSize)} (Err: 2)");
|
|
uploadResult.ErrorCode = 2;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
DateTime oggi = DateTime.Today;
|
|
relDir = _configuration["ServerConf:FileShareAppBackup"];
|
|
fileDir = Path.Combine(relDir, CodApp, CodInst);
|
|
if (!Directory.Exists(fileDir))
|
|
{
|
|
Directory.CreateDirectory(fileDir);
|
|
}
|
|
var filePath = Path.Combine(fileDir, ZipFile.FileName);
|
|
|
|
// elimino se ci fosse già il file...
|
|
if (System.IO.File.Exists(filePath))
|
|
{
|
|
System.IO.File.Delete(filePath);
|
|
}
|
|
|
|
// salvo da filestream a file locale
|
|
using (FileStream fs = new(filePath, FileMode.Create))
|
|
{
|
|
await ZipFile.CopyToAsync(fs);
|
|
}
|
|
|
|
// log!
|
|
Log.Info($"{ZipFile.FileName} saved at {filePath}");
|
|
uploadResult.Uploaded = true;
|
|
uploadResult.StoredFileName = ZipFile.FileName;
|
|
// se richiesto unzip eseguo
|
|
if (DoUnzip)
|
|
{
|
|
bool extractDone = false;
|
|
// recupero applicativi connessi
|
|
var listLic = await dataService.AppDtoSearch(CodInst, CodApp, false);
|
|
var currLic = listLic.Where(x => x.IsActive).FirstOrDefault();
|
|
// procedo SOLO SE ho una licenza attiva x questo cliente
|
|
if (currLic != null)
|
|
{
|
|
//recupero authKey
|
|
authKey = currLic.Chiave;
|
|
using (ZipFile zf = new ZipFile(filePath))
|
|
{
|
|
zf.Password = authKey;
|
|
foreach (ZipEntry zipEntry in zf)
|
|
{
|
|
// Manipulate the output filename here as desired.
|
|
var entryFileName = zipEntry.Name;
|
|
var fullZipToPath = Path.Combine(fileDir, entryFileName);
|
|
Console.WriteLine(zipEntry.Name);
|
|
if (!zipEntry.IsFile)
|
|
{
|
|
Directory.CreateDirectory(fullZipToPath);
|
|
// Ignore directories
|
|
continue;
|
|
}
|
|
|
|
// 4K is optimum
|
|
var buffer = new byte[4096];
|
|
|
|
// Unzip file in buffered chunks. This is just as fast as
|
|
// unpacking to a buffer the full size of the file, but does not
|
|
// waste memory. The "using" will close the stream even if an
|
|
// exception occurs.
|
|
using (var zipStream = zf.GetInputStream(zipEntry))
|
|
{
|
|
using (Stream fsOutput = System.IO.File.Create(fullZipToPath))
|
|
{
|
|
StreamUtils.Copy(zipStream, fsOutput, buffer);
|
|
}
|
|
}
|
|
extractDone = true;
|
|
}
|
|
}
|
|
// elimino zip e altro...
|
|
if (extractDone)
|
|
{
|
|
System.IO.File.Delete(filePath);
|
|
}
|
|
|
|
// se richiesta auto approvazione eseguo...
|
|
string reqAppFile = Path.Combine(fileDir, "ChangeApprove.req");
|
|
if (System.IO.File.Exists(reqAppFile))
|
|
{
|
|
System.IO.File.Delete(reqAppFile);
|
|
}
|
|
if (ForceApprov)
|
|
{
|
|
// FixMe ToDo !!!
|
|
// deve chiamare metodo x approvare, in MP-PROG, la directory dei
|
|
// documenti caricati.. x ora segnaposto con file (così se scansiona
|
|
// trova e approva)...
|
|
System.IO.File.WriteAllText(reqAppFile, authKey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (IOException ex)
|
|
{
|
|
Log.Error($"{ZipFile.FileName} error on upload (Err: 3): {ex.Message}");
|
|
uploadResult.ErrorCode = 3;
|
|
}
|
|
}
|
|
uploadResults.Add(uploadResult);
|
|
Log.Info($"ZipUpload backup completed | CodInst: {CodInst} | CodApp: {CodApp} | {uploadResults.Count} files");
|
|
|
|
return new CreatedResult(resourcePath, uploadResult);
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Protected Properties
|
|
|
|
/// <summary>
|
|
/// Dataservice x accesso DB
|
|
/// </summary>
|
|
protected ApiDataService dataService { get; set; }
|
|
|
|
#endregion Protected Properties
|
|
|
|
#region Protected Methods
|
|
|
|
/// <summary>
|
|
/// Restituisce size calcolata
|
|
/// </summary>
|
|
/// <param name="origSize"></param>
|
|
/// <returns></returns>
|
|
protected string CalcSize(long origSize)
|
|
{
|
|
return MeasureUtils.SizeSuffix(origSize, 1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calcolo correetto mimetype da nome file
|
|
/// </summary>
|
|
/// <param name="fileName"></param>
|
|
/// <returns></returns>
|
|
protected string GetMimeType(string fileName)
|
|
{
|
|
var provider = new FileExtensionContentTypeProvider();
|
|
string contentType;
|
|
if (!provider.TryGetContentType(fileName, out contentType))
|
|
{
|
|
contentType = "application/octet-stream";
|
|
}
|
|
return contentType;
|
|
}
|
|
|
|
#endregion Protected Methods
|
|
|
|
#region Private Fields
|
|
|
|
private static IConfiguration _configuration;
|
|
|
|
/// <summary>
|
|
/// Classe per logging
|
|
/// </summary>
|
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
|
|
|
private readonly IWebHostEnvironment env;
|
|
|
|
#endregion Private Fields
|
|
}
|
|
} |