diff --git a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs index 847a163b..a48230e4 100644 --- a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs +++ b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs @@ -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 { diff --git a/EgwCoreLib.Lux.Data/Services/ImageCacheService.cs b/EgwCoreLib.Lux.Data/Services/ImageCacheService.cs index 08419848..8e228255 100644 --- a/EgwCoreLib.Lux.Data/Services/ImageCacheService.cs +++ b/EgwCoreLib.Lux.Data/Services/ImageCacheService.cs @@ -223,6 +223,19 @@ namespace EgwCoreLib.Lux.Data.Services return $"{rawVal}"; } + /// + /// Recupera img redis PNG x id item richiesto + /// + /// UID item + /// Environment item + public async Task 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}"; + } + /// /// Recupera img redis SVG x id item richiesto /// diff --git a/Lux.API/Controllers/ImageController.cs b/Lux.API/Controllers/ImageController.cs index 924faf67..4a83e51c 100644 --- a/Lux.API/Controllers/ImageController.cs +++ b/Lux.API/Controllers/ImageController.cs @@ -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 logger) + public ImageController(IConfiguration config, ImageCacheService imgServ, ILogger logger) { + _config = config; _imgService = imgServ; _logger = logger; + // setup params + basePath = _config.GetValue("ServerConf:FileSharePath") ?? "unsafe_uploads"; } #endregion Public Constructors #region Public Methods + /// + /// 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 + /// + /// uid oggetto + /// environment oggetto + /// + [HttpGet("{id}")] + [HttpGet("cache/{id}")] + public async Task 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); + } + /// /// 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"); } + /// + /// Chiamata GET: restituisce file SVG/PNG (da file, area static) + /// GET: api/image/static/SP.000000000005.jpg + /// GET: api/image/static/SP.000000000006.svg + /// + /// uid oggetto + /// + [HttpGet("static/{id}")] + public async Task 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); + } + /// /// 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 /// /// id univoco img /// @@ -71,79 +261,18 @@ namespace Lux.API.Controllers return File(bytes, "image/svg+xml"); } - /// - /// 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 - /// - /// uid oggetto - /// environment oggetto - /// - [HttpGet("{id}")] - [HttpGet("cache/{id}")] - //[HttpGet("file/{id}")] - public async Task 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 _logger; + private IConfiguration _config; + + /// + /// Base path x network share files + /// + 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 + + /// + /// ETag semplice basato su hash + /// + /// + /// + 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 } } \ No newline at end of file diff --git a/Lux.API/Lux.API.csproj b/Lux.API/Lux.API.csproj index 972f87e4..3b534e54 100644 --- a/Lux.API/Lux.API.csproj +++ b/Lux.API/Lux.API.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - 1.1.2603.1205 + 1.1.2603.1207 diff --git a/Lux.UI/Lux.UI.csproj b/Lux.UI/Lux.UI.csproj index 8f50bf4b..de6f460b 100644 --- a/Lux.UI/Lux.UI.csproj +++ b/Lux.UI/Lux.UI.csproj @@ -5,7 +5,7 @@ enable enable aspnet-Lux.UI-a758c101-a2f4-4e38-977d-1c4887dbbd50 - 1.1.2603.1205 + 1.1.2603.1207 diff --git a/Resources/ChangeLog.html b/Resources/ChangeLog.html index 4cb9a08a..7f6402c5 100644 --- a/Resources/ChangeLog.html +++ b/Resources/ChangeLog.html @@ -1,6 +1,6 @@ LUX - Web Windows MES -

Versione: 1.1.2603.1205

+

Versione: 1.1.2603.1207


Note di rilascio:
  • diff --git a/Resources/VersNum.txt b/Resources/VersNum.txt index 2205792a..73b6257b 100644 --- a/Resources/VersNum.txt +++ b/Resources/VersNum.txt @@ -1 +1 @@ -1.1.2603.1205 +1.1.2603.1207 diff --git a/Resources/manifest.xml b/Resources/manifest.xml index 28e4dc6e..d36d2ed9 100644 --- a/Resources/manifest.xml +++ b/Resources/manifest.xml @@ -1,6 +1,6 @@ - 1.1.2603.1205 + 1.1.2603.1207 http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html false