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
{
///
/// Controller caricamento file
///
[ApiController]
[Route("api/filesave")]
public class FilesaveController : ControllerBase
{
#region Public Constructors
///
/// Init generico
///
///
///
///
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
///
/// Recupera un singolo file dato ticket code + nome file (safe)
///
/// Ticket code formato T00000000
/// Nome file (safe)
/// Nome file da scaricare
///
[HttpGet("{id}/{secureName}/{fileName}")]
public async Task 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");
}
}
///
/// Elenco files associati a ticket supporto POST api/filesave/list/1
///
///
///
///
[HttpPost("list/{id}")]
public async Task> list(int id, [FromBody] SupportRequest CurrRequest)
{
List result = new List();
// 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;
}
///
/// Caricamento file effettivo via POST
///
/// TicketId x riferimento
/// Elenco files da caricare
///
[HttpPost()]
public async Task>> PostFiles([FromForm] int ticketId, [FromForm] IEnumerable 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 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);
}
///
/// Caricamento file effettivo via POST
///
/// TicketId x riferimento
/// Elenco files da caricare
///
[HttpPost("single")]
public async Task> 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 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);
}
///
/// Caricamento file backup applicativo in formato ZIP via FORM POST (backup configurazione applicativo, file protetto da masterKey)
///
/// Applicazione di riferimento
/// Installazione di riferimento
/// Richiesta UnZip file post caricamento
///
/// Richiesta di approvazione salvataggio files modificati post upload + unzip
///
/// File da caricare ed estrarre
///
[HttpPost("zipbackup")]
public async Task> 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 uploadResults = new List();
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
///
/// Dataservice x accesso DB
///
protected ApiDataService dataService { get; set; }
#endregion Protected Properties
#region Protected Methods
///
/// Restituisce size calcolata
///
///
///
protected string CalcSize(long origSize)
{
return MeasureUtils.SizeSuffix(origSize, 1);
}
///
/// Calcolo correetto mimetype da nome file
///
///
///
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;
///
/// Classe per logging
///
private static Logger Log = LogManager.GetCurrentClassLogger();
private readonly IWebHostEnvironment env;
#endregion Private Fields
}
}