using MiniWebApi.Utilities; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; using Vinno.IUS.Common.Log; namespace Vinno.vCloud.Disk { internal class ViewEngine { public DateTime LastModifiedTime { get; set; } private Dictionary _fileSourceDic; private string _namespace; /// /// 是否为局域网内部署 /// public bool IsLocalServer { get; set; } = false; public ViewEngine() { _namespace = GetType().Namespace; _fileSourceDic = new Dictionary(); var fileList = GetType().Assembly.GetManifestResourceNames(); foreach (string fileName in fileList) { if (string.IsNullOrWhiteSpace(fileName)) continue; if (!fileName.ToLower().StartsWith(_namespace.ToLower())) continue; string key = fileName.Substring(_namespace.Length).ToLower(); _fileSourceDic.Add(key, fileName); _fileSourceDic.Add($".{key}", fileName); } } /// /// 获取资源文件 /// /// /// public Stream GetResourceFile(string filePath) { filePath = filePath?.Replace("/", ".").ToLower(); if (!filePath.StartsWith(".")) filePath = "." + filePath; if (!_fileSourceDic.ContainsKey(filePath)) return Stream.Null; return GetType().Assembly.GetManifestResourceStream(_fileSourceDic[filePath]); } /// /// 读取文件 /// /// /// /// /// private List GetFileSource(string extType, object model, Stream fileStream) { List byteList = new List(); if (fileStream != null) { using (fileStream) { byte[] bytes = new byte[8192]; int readCount = 0; do { readCount = fileStream.Read(bytes, 0, bytes.Length); byteList.AddRange(bytes.Take(readCount)); } while (readCount > 0); bytes = null; } } return byteList; } /// /// 获取资源文件 /// /// /// private List GetResourceFile(string filePath, string extType, object model = null) { filePath = filePath?.Replace("/", ".").ToLower(); if (!_fileSourceDic.ContainsKey(filePath)) return new List(); using (var fileStream = GetType().Assembly.GetManifestResourceStream(_fileSourceDic[filePath])) { return GetFileSource(extType, model, fileStream); } } /// /// 读取某个路径下的文件 /// /// /// private List GetPathFile(string filePath, string extType, object model = null) { if (!string.IsNullOrWhiteSpace(filePath)) { var currentDir = AppDomain.CurrentDomain.BaseDirectory; filePath = Path.Combine(currentDir, filePath.Replace("/", "\\")); if (File.Exists(filePath)) { using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { return GetFileSource(extType, model, fileStream); } } } return new List(); } /// /// 获取资源文件文本内容 /// /// /// /// public string GetResourceFileStr(string filePath, string extType) { return Encoding.UTF8.GetString(GetResourceFile(filePath, extType).ToArray()); } /// /// 指定路径得到文件 /// /// /// /// /// public List GetSource(string filePath, string mineType, object model = null) { var filePathWordLower = filePath.ToLower(); //如果时内网部署则使用内网的easyEchartLocal.js if (IsLocalServer && filePathWordLower.IndexOf("/modules/easyEchart.js", StringComparison.CurrentCultureIgnoreCase) > -1) { filePath = Regex.Replace(filePath, ".*(/modules/easyEchart[.]js).*", EasyEchartReplace, RegexOptions.IgnoreCase); } var buffer = GetResourceFile(filePath, mineType, model); if (buffer.Count == 0) { buffer = GetPathFile(filePath, mineType, model); } return buffer; } /// /// 替换Echart文件路径 /// /// /// private string EasyEchartReplace(Match match) { string newJSPath = match.Value; if (match.Success && match.Groups.Count > 1) { newJSPath = match.Value.Replace(match.Groups[1].Value, "/modules/easyEchartLocal.js"); } return newJSPath; } /// /// 查找资源文件 /// /// /// public bool FindSourceNoModel(HttpListenerContext context) { bool userCache = true; var lastModifiedTime = LastModifiedTime; var mineType = "html/text"; if (context.Request.RawUrl == "/") { context.Response.Headers.Add("Location", "/index.html"); context.Response.StatusCode = 302; context.Response.Close(); return true; } var match = Regex.Match(context.Request.RawUrl, "\\S*[\\.](css|gif|html|json|js|eot|svg|ttf|woff|woff2|jpg|png|ico)"); if (match.Success && match.Groups.Count > 1) { //服务器启动版本是否更改 if (context.Request.Headers["If-Modified-Since"].ToStringEx() == lastModifiedTime.ToUniversalTime().ToString("r")) { context.Response.StatusCode = 304; context.Response.OutputStream.Close(); return true; } var buffer = GetSource(match.Value, match.Groups[1].Value, null).ToArray(); var content = Encoding.UTF8.GetString(buffer); if (buffer.Length > 0) { mineType = MineTypeJsonHelper.GetJsonRoot().GetJPropertyString($"$.{match.Groups[1].Value}"); if (userCache && match.Groups[0].Value.IndexOf("/index.html", StringComparison.CurrentCultureIgnoreCase) == -1) { //设置Last-Modified标识最后的文件版本 context.Response.AddHeader("Last-Modified", lastModifiedTime.ToUniversalTime().ToString("r")); //设置缓存超时时间 context.Response.AddHeader("Cache-Control", "max-age=300"); } //context.Response.ContentEncoding = Encoding.UTF8; context.Response.ContentType = mineType + (match.Groups[1].Value == "html" ? ";charset=utf-8" : ""); if (context.Request.QueryString.Get("IsDownloadType") == "1") { context.Response.ContentType = "application/octet-stream"; } context.Response.ProtocolVersion = new Version("1.1"); if (!Regex.IsMatch(context.Request.RawUrl, "\\S*[\\.](gif|eot|svg|ttf|woff|woff2|jpg|png|ico)")) { ChooseCompress(context, buffer); } else { context.Response.ContentLength64 = buffer.Length; if (context.Response.OutputStream.CanWrite && context.Request.HttpMethod.ToUpper() != "HEAD") { context.Response.OutputStream.Write(buffer, 0, buffer.Length); } else { Logger.WriteLineWarn($"vcloud disk - http method err, url:{context?.Request?.Url}"); } } context.Response.OutputStream.Close(); buffer = null; return true; } } return false; } private void ChooseCompress(HttpListenerContext context, byte[] data) { context.Response.AddHeader("Content-Encoding", "deflate"); CompressDeflate(context, data); return; } private void CompressDeflate(HttpListenerContext context, byte[] data) { using (MemoryStream memory = new MemoryStream()) { using (var zipStream = new DeflateStream(memory, CompressionMode.Compress, true)) { zipStream.Write(data, 0, data.Length); zipStream.Close(); byte[] buffers = memory.ToArray(); context.Response.ContentLength64 = buffers.Length; if (context.Response.OutputStream.CanWrite && context.Request.HttpMethod.ToUpper() != "HEAD") { context.Response.OutputStream.Write(buffers, 0, buffers.Length); } else { Logger.WriteLineWarn($"vcloud disk - http method err, url:{context?.Request?.Url}"); } } } } } }