Prima versione con img statiche + caching control
This commit is contained in:
@@ -4443,6 +4443,18 @@ namespace EgwCoreLib.Lux.Data.Controllers
|
||||
currRec.FileResource = updRec.FileResource;
|
||||
currRec.FileSize = updRec.FileSize;
|
||||
#endif
|
||||
// check/fix tipo file..
|
||||
if (updRec.ImgType == ImageType.ND)
|
||||
{
|
||||
if (updRec.SourceType == ItemSourceType.Jwd || updRec.SourceType == ItemSourceType.FileBTL)
|
||||
{
|
||||
updRec.ImgType = ImageType.Calculated;
|
||||
}
|
||||
else
|
||||
{
|
||||
updRec.ImgType = ImageType.Fixed;
|
||||
}
|
||||
}
|
||||
dbCtx.Entry(currRec).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
@@ -4464,6 +4476,18 @@ namespace EgwCoreLib.Lux.Data.Controllers
|
||||
//using (DataLayerContext dbCtx = new DataLayerContext(_config))
|
||||
using (DataLayerContext dbCtx = new DataLayerContext())
|
||||
{
|
||||
// check/fix tipo file..
|
||||
if (currRec.ImgType == ImageType.ND)
|
||||
{
|
||||
if (currRec.SourceType == ItemSourceType.Jwd || currRec.SourceType == ItemSourceType.FileBTL)
|
||||
{
|
||||
currRec.ImgType = ImageType.Calculated;
|
||||
}
|
||||
else
|
||||
{
|
||||
currRec.ImgType = ImageType.Fixed;
|
||||
}
|
||||
}
|
||||
//try
|
||||
//{
|
||||
var dbResult = await dbCtx
|
||||
@@ -4472,6 +4496,7 @@ namespace EgwCoreLib.Lux.Data.Controllers
|
||||
.FirstOrDefaultAsync();
|
||||
if (dbResult != null)
|
||||
{
|
||||
#if false
|
||||
//dbCtx.DbSetItem.Remove(newCount);
|
||||
dbResult.Cost = currRec.Cost;
|
||||
dbResult.Description = currRec.Description;
|
||||
@@ -4486,7 +4511,11 @@ namespace EgwCoreLib.Lux.Data.Controllers
|
||||
dbResult.SourceType = currRec.SourceType;
|
||||
dbResult.SupplCode = currRec.SupplCode;
|
||||
dbResult.UM = currRec.UM;
|
||||
dbCtx.Entry(dbResult).State = EntityState.Modified;
|
||||
dbResult.ImgType = currRec.ImgType;
|
||||
dbCtx.Entry(dbResult).State = EntityState.Modified;
|
||||
#endif
|
||||
|
||||
dbCtx.Entry(dbResult).CurrentValues.SetValues(currRec);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -223,6 +223,19 @@ namespace EgwCoreLib.Lux.Data.Services
|
||||
return $"{rawVal}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera img redis PNG x id item richiesto
|
||||
/// </summary>
|
||||
/// <param name="imgUid">UID item</param>
|
||||
/// <param name="envir">Environment item</param>
|
||||
public async Task<string> LoadPngAsync(string imgUid, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW)
|
||||
{
|
||||
// recupero img da cache
|
||||
string currKey = $"{redisBaseKey}:{envir}:Img:Png:{imgUid.Replace("/", ":")}";
|
||||
var rawVal = await _redisService.GetAsync(currKey);
|
||||
return $"{rawVal}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera img redis SVG x id item richiesto
|
||||
/// </summary>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using EgwCoreLib.Lux.Data.Services;
|
||||
using EgwMultiEngineManager.Data;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lux.API.Controllers
|
||||
@@ -15,16 +15,86 @@ namespace Lux.API.Controllers
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public ImageController(ImageCacheService imgServ, ILogger<ImageController> logger)
|
||||
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
|
||||
@@ -52,9 +122,129 @@ namespace Lux.API.Controllers
|
||||
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.IsNullOrEmpty(id))
|
||||
return NotFound();
|
||||
|
||||
string filePath = Path.Combine(basePath, "static", id);
|
||||
|
||||
if (!System.IO.File.Exists(filePath))
|
||||
return NotFound();
|
||||
|
||||
// vers 1
|
||||
#if false
|
||||
string mimeType = "txt";
|
||||
byte[] bytes = new byte[0];
|
||||
string extension = Path.GetExtension(id);
|
||||
string rawContent = "";
|
||||
if (System.IO.File.Exists(filePath))
|
||||
{
|
||||
// fix testo/binario...
|
||||
switch (extension)
|
||||
{
|
||||
case ".svg":
|
||||
mimeType = "image/svg+xml";
|
||||
rawContent = await System.IO.File.ReadAllTextAsync(filePath);
|
||||
bytes = Encoding.UTF8.GetBytes(rawContent);
|
||||
break;
|
||||
|
||||
case ".png":
|
||||
mimeType = "image/png";
|
||||
bytes = await System.IO.File.ReadAllBytesAsync(filePath);
|
||||
break;
|
||||
|
||||
case ".jpg":
|
||||
mimeType = "image/jpg";
|
||||
bytes = await System.IO.File.ReadAllBytesAsync(filePath);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//vers 2 ok
|
||||
#if false
|
||||
string extension = Path.GetExtension(id).ToLowerInvariant();
|
||||
string mimeType = extension switch
|
||||
{
|
||||
".svg" => "image/svg+xml",
|
||||
".png" => "image/png",
|
||||
".jpg" => "image/jpeg",
|
||||
".jpeg" => "image/jpeg",
|
||||
".gif" => "image/gif",
|
||||
_ => "application/octet-stream"
|
||||
};
|
||||
|
||||
byte[] bytes = await System.IO.File.ReadAllBytesAsync(filePath);
|
||||
#endif
|
||||
|
||||
// MIME automatico
|
||||
var provider = new FileExtensionContentTypeProvider();
|
||||
if (!provider.TryGetContentType(filePath, out string mimeType))
|
||||
mimeType = "application/octet-stream";
|
||||
|
||||
#if false
|
||||
// Lettura corretta (sempre binaria)
|
||||
byte[] bytes = await System.IO.File.ReadAllBytesAsync(filePath);
|
||||
#endif
|
||||
|
||||
// ETag (opzionale, vedi sotto)
|
||||
string etag = GenerateETag(filePath);
|
||||
#if false
|
||||
string etag = GenerateETag(bytes);
|
||||
#endif
|
||||
Response.Headers["ETag"] = etag;
|
||||
|
||||
// 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";
|
||||
|
||||
// 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");
|
||||
#if false
|
||||
return File(bytes, mimeType);
|
||||
#endif
|
||||
return File(stream, mimeType, enableRangeProcessing: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chiamata GET: riceve Json in formato JwdDto, restituisce svg file
|
||||
/// GET: api/Jwd/svg/00000000-0000-0000-0000-000000000000
|
||||
/// GET: api/image/svg/00000000-0000-0000-0000-000000000000
|
||||
/// </summary>
|
||||
/// <param name="id">id univoco img</param>
|
||||
/// <returns></returns>
|
||||
@@ -71,79 +261,18 @@ namespace Lux.API.Controllers
|
||||
return File(bytes, "image/svg+xml");
|
||||
}
|
||||
|
||||
/// <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}")]
|
||||
//[HttpGet("file/{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);
|
||||
}
|
||||
|
||||
|
||||
#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
|
||||
|
||||
@@ -152,5 +281,29 @@ namespace Lux.API.Controllers
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Version>1.1.2603.1205</Version>
|
||||
<Version>1.1.2603.1207</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>aspnet-Lux.UI-a758c101-a2f4-4e38-977d-1c4887dbbd50</UserSecretsId>
|
||||
<Version>1.1.2603.1205</Version>
|
||||
<Version>1.1.2603.1207</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>LUX - Web Windows MES</i>
|
||||
<h4>Versione: 1.1.2603.1205</h4>
|
||||
<h4>Versione: 1.1.2603.1207</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.1.2603.1205
|
||||
1.1.2603.1207
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>1.1.2603.1205</version>
|
||||
<version>1.1.2603.1207</version>
|
||||
<url>http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip</url>
|
||||
<changelog>http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
Reference in New Issue
Block a user