249 lines
9.3 KiB
C#
249 lines
9.3 KiB
C#
using EgwCoreLib.Lux.Data.Services;
|
|
using EgwMultiEngineManager.Data;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.StaticFiles;
|
|
using NLog;
|
|
using System.Diagnostics;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
|
|
namespace Lux.API.Controllers
|
|
{
|
|
[Route("api/[controller]")]
|
|
[ApiController]
|
|
public class ImageController : ControllerBase
|
|
{
|
|
#region Public Constructors
|
|
|
|
public ImageController(IConfiguration config, ImageCacheService imgServ, ILogger<ImageController> logger)
|
|
{
|
|
_config = config;
|
|
_imgService = imgServ;
|
|
_logger = logger;
|
|
// setup params
|
|
basePath = _config.GetValue<string>("ServerConf:FileSharePath") ?? "unsafe_uploads";
|
|
}
|
|
|
|
#endregion Public Constructors
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Chiamata GET: restituisce file SVG/PNG (da file o cache REDIS), eliminando nome rand (x force refresh)
|
|
/// GET: api/image/OFF0000001.001.svg?env=WINDOW
|
|
/// GET: api/image/cache/OFF0000001.001.svg?env=WINDOW
|
|
/// GET: api/image/OFF0000002.001.png?env=WINDOW
|
|
/// GET: api/image/cache/OFF0000002.001.png?env=WINDOW
|
|
/// GET: api/image/OFF0000002.002-123456.png?env=WINDOW
|
|
/// GET: api/image/cache/OFF0000002.002-123456.png?env=WINDOW
|
|
/// </summary>
|
|
/// <param name="id">uid oggetto</param>
|
|
/// <param name="env">environment oggetto</param>
|
|
/// <returns></returns>
|
|
[HttpGet("{id}")]
|
|
[HttpGet("cache/{id}")]
|
|
public async Task<IActionResult> cacheFile(string id, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS env = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW)
|
|
{
|
|
Stopwatch sw = new Stopwatch();
|
|
sw.Start();
|
|
|
|
if (string.IsNullOrEmpty(id))
|
|
return NotFound();
|
|
|
|
string mimeType = "txt";
|
|
byte[] bytes = new byte[0];
|
|
// ...se ricevo percorso --> leggo jwd/svg cablato
|
|
if (!string.IsNullOrEmpty(id))
|
|
{
|
|
// se contiene i caratteri casuali x forzare reload --> li levo
|
|
if (id.Contains("-"))
|
|
{
|
|
id = id.Substring(0, id.IndexOf("-"));
|
|
}
|
|
|
|
// secondo del tipo + envr decodifico valore corretto
|
|
switch (env)
|
|
{
|
|
case Constants.EXECENVIRONMENTS.NULL:
|
|
break;
|
|
|
|
case Constants.EXECENVIRONMENTS.WINDOW:
|
|
mimeType = "image/svg+xml";
|
|
string svgContent = await _imgService.LoadSvgAsync(id, env);
|
|
// se vuoto --> leggo img logo...
|
|
if (string.IsNullOrEmpty(svgContent))
|
|
{
|
|
string filePath = Path.Combine("DemoImg", "LogoEgalware.svg");
|
|
svgContent = await System.IO.File.ReadAllTextAsync(filePath);
|
|
}
|
|
bytes = Encoding.UTF8.GetBytes(svgContent);
|
|
break;
|
|
|
|
case Constants.EXECENVIRONMENTS.BEAM:
|
|
case Constants.EXECENVIRONMENTS.WALL:
|
|
case Constants.EXECENVIRONMENTS.CABINET:
|
|
default:
|
|
mimeType = "image/png";
|
|
string base64Encoded = _imgService.LoadPng(id, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM);
|
|
// converto base64
|
|
bytes = Convert.FromBase64String(base64Encoded);
|
|
break;
|
|
}
|
|
}
|
|
sw.Stop();
|
|
Log.Info($"{mimeType} | {sw.Elapsed.TotalMilliseconds:N3} ms");
|
|
return File(bytes, mimeType);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chiamata GET: restituisce file PNG (da file o da cache)
|
|
/// PUT: api/image/png/00000000-0000-0000-0000-000000000000
|
|
/// </summary>
|
|
/// <param name="id">id oggetto</param>
|
|
/// <returns></returns>
|
|
[HttpGet("png/{id}")]
|
|
public async Task<IActionResult> png(string id)
|
|
{
|
|
Stopwatch sw = new Stopwatch();
|
|
sw.Start();
|
|
string base64Encoded = "";
|
|
byte[] decodedBytes = new byte[0];
|
|
// ...se ricevo percorso --> leggo jwd/svg cablato
|
|
if (!string.IsNullOrEmpty(id))
|
|
{
|
|
// bonifica nome svg da
|
|
base64Encoded = _imgService.LoadPng(id, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM);
|
|
// converto base64
|
|
decodedBytes = Convert.FromBase64String(base64Encoded);
|
|
}
|
|
sw.Stop();
|
|
Log.Info($"pngString | {sw.Elapsed.TotalMilliseconds:N3} ms");
|
|
//return Ok(decodedString);
|
|
return File(decodedBytes, "image/png");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chiamata GET: restituisce file SVG/PNG (da file, area static)
|
|
/// GET: api/image/static/SP.000000000005.jpg
|
|
/// GET: api/image/static/SP.000000000006.svg
|
|
/// </summary>
|
|
/// <param name="id">uid oggetto</param>
|
|
/// <returns></returns>
|
|
[HttpGet("static/{id}")]
|
|
public async Task<IActionResult> StaticFile(string id)
|
|
{
|
|
Stopwatch sw = new Stopwatch();
|
|
sw.Start();
|
|
|
|
if (string.IsNullOrWhiteSpace(id))
|
|
return NotFound();
|
|
|
|
string filePath = Path.Combine(basePath, "static", id);
|
|
|
|
if (!System.IO.File.Exists(filePath))
|
|
return NotFound();
|
|
|
|
// MIME automatico
|
|
var provider = new FileExtensionContentTypeProvider();
|
|
if (!provider.TryGetContentType(filePath, out string mimeType))
|
|
mimeType = "application/octet-stream";
|
|
|
|
// Last-Modified
|
|
var lastModified = System.IO.File.GetLastWriteTimeUtc(filePath);
|
|
Response.Headers["Last-Modified"] = lastModified.ToString("R");
|
|
|
|
// Cache-Control (1 giorno)
|
|
Response.Headers["Cache-Control"] = "public,max-age=86400";
|
|
|
|
// ETag
|
|
string etag = GenerateETag(filePath);
|
|
Response.Headers["ETag"] = etag;
|
|
|
|
// Se il client ha già la versione aggiornata → 304
|
|
if (Request.Headers.TryGetValue("If-None-Match", out var inm) &&
|
|
inm.ToString() == etag)
|
|
{
|
|
return StatusCode(StatusCodes.Status304NotModified);
|
|
}
|
|
|
|
// Se il client usa If-Modified-Since
|
|
if (Request.Headers.TryGetValue("If-Modified-Since", out var ims) &&
|
|
DateTime.TryParse(ims, out var since) &&
|
|
Math.Abs((lastModified - since.ToUniversalTime()).TotalSeconds) < 1)
|
|
{
|
|
return StatusCode(StatusCodes.Status304NotModified);
|
|
}
|
|
|
|
// Streaming ottimizzato (range support)
|
|
var stream = System.IO.File.OpenRead(filePath);
|
|
|
|
sw.Stop();
|
|
Log.Info($"{mimeType} | {sw.Elapsed.TotalMilliseconds:N3} ms");
|
|
return File(stream, mimeType, enableRangeProcessing: true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chiamata GET: riceve Json in formato JwdDto, restituisce svg file
|
|
/// GET: api/image/svg/00000000-0000-0000-0000-000000000000
|
|
/// </summary>
|
|
/// <param name="id">id univoco img</param>
|
|
/// <returns></returns>
|
|
[HttpGet("svg/{id}")]
|
|
public async Task<IActionResult> svgFileGet(string id)
|
|
{
|
|
Stopwatch sw = new Stopwatch();
|
|
sw.Start();
|
|
string filePath = Path.Combine("DemoImg", "AntaDoppia.svg");
|
|
var svgContent = await System.IO.File.ReadAllTextAsync(filePath);
|
|
var bytes = System.Text.Encoding.UTF8.GetBytes(svgContent);
|
|
sw.Stop();
|
|
_logger.LogInformation($"svgString | {sw.Elapsed.TotalMilliseconds:N3} ms");
|
|
return File(bytes, "image/svg+xml");
|
|
}
|
|
|
|
#endregion Public Methods
|
|
|
|
#region Private Fields
|
|
|
|
private static Logger Log = LogManager.GetCurrentClassLogger();
|
|
private readonly ILogger<ImageController> _logger;
|
|
private IConfiguration _config;
|
|
|
|
/// <summary>
|
|
/// Base path x network share files
|
|
/// </summary>
|
|
private string basePath = "unsafe_uploads";
|
|
|
|
#endregion Private Fields
|
|
|
|
#region Private Properties
|
|
|
|
private ImageCacheService _imgService { get; set; }
|
|
|
|
#endregion Private Properties
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// ETag semplice basato su hash
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
private string GenerateETag(byte[] data)
|
|
{
|
|
using var sha = System.Security.Cryptography.SHA256.Create();
|
|
var hash = sha.ComputeHash(data);
|
|
return "\"" + Convert.ToBase64String(hash) + "\"";
|
|
}
|
|
|
|
private string GenerateETag(string filePath)
|
|
{
|
|
using var sha = SHA256.Create();
|
|
using var stream = System.IO.File.OpenRead(filePath);
|
|
var hash = sha.ComputeHash(stream);
|
|
return "\"" + Convert.ToBase64String(hash) + "\"";
|
|
}
|
|
|
|
#endregion Private Methods
|
|
}
|
|
} |