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