v9.10.2019.0101  全面巩固批量Insert/Update/Upsert,支持数据备份、恢复和同步,支持实体列表保存到文件以及加载
大石头 authored at 2019-01-01 13:38:33
2.60 KiB
X
using System.Net;
using System.Reflection;
using NewLife.Data;

namespace NewLife.Http;

/// <summary>内嵌资源文件处理器</summary>
/// <remarks>
/// 将程序集中的嵌入资源映射为 HTTP 静态文件服务。
/// 
/// 例如注册 /panel/ 对应资源前缀 MyApp.Resources.Panel,
/// 则请求 GET /panel/css/style.css 将加载嵌入资源 MyApp.Resources.Panel.css.style.css
/// </remarks>
public class EmbeddedFileHandler : IHttpHandler
{
    #region 属性
    /// <summary>映射路径。如 /panel/</summary>
    public String Path { get; set; } = null!;

    /// <summary>资源名前缀。如 MyApp.Resources.Panel</summary>
    public String ContentPath { get; set; } = null!;

    /// <summary>资源所在的程序集。为 null 时使用入口程序集</summary>
    public Assembly? Assembly { get; set; }

    /// <summary>默认文档。当请求路径为目录时返回的默认文件名,默认 index.html</summary>
    public String DefaultDocument { get; set; } = "index.html";
    #endregion

    /// <summary>处理请求</summary>
    /// <param name="ctx">Http上下文</param>
    public virtual Task ProcessRequestAsync(IHttpContext ctx)
    {
        var file = ctx.Path;
        if (file.StartsWithIgnoreCase(Path))
            file = file[Path.Length..];

        // 默认文档
        if (file.IsNullOrEmpty() || file == "/")
            file = DefaultDocument;

        // 安全:防止路径穿越
        if (file.Contains(".."))
        {
            ctx.Response.StatusCode = HttpStatusCode.NotFound;
            return TaskEx.CompletedTask;
        }

        // 获取目标程序集
        var asm = Assembly ?? Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
        if (asm == null)
        {
            ctx.Response.StatusCode = HttpStatusCode.NotFound;
            return TaskEx.CompletedTask;
        }

        // 将路径转换为嵌入资源名称(/ → .)
        var resourceName = $"{ContentPath}.{file.Replace('/', '.').Replace('\\', '.')}";

        var stream = asm.GetManifestResourceStream(resourceName);
        if (stream == null)
        {
            ctx.Response.StatusCode = HttpStatusCode.NotFound;
            return TaskEx.CompletedTask;
        }

        var ext = System.IO.Path.GetExtension(file) ?? "";
        var contentType = MimeHelper.GetContentType(ext) ?? "application/octet-stream";

        // 流式发送资源内容:流所有权移交响应(发送完成后释放),大资源不物化到内存
        ctx.Response.ContentType = contentType;
        ctx.Response.BodyStream = stream;

        return TaskEx.CompletedTask;
    }
}