减少TraceItem数据量较大时的性能浪费
智能大石头 authored at 2024-10-22 08:41:14
3.14 KiB
Stardust
using System;
using System.IO;
using System.Linq;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using NewLife;
using NewLife.Log;
using NewLife.Web;

namespace Stardust.Extensions.Caches;

/// <summary>
/// 文件缓存扩展
/// </summary>
public static class FileCacheExtensions
{
    /// <summary>使用文件缓存,把指定目录映射到url上,并提供上级地址用于下载不存在的文件</summary>
    /// <param name="app"></param>
    /// <param name="requestPath">请求虚拟路径。如/files</param>
    /// <param name="localPath">本地缓存目录</param>
    /// <param name="getWhiteIP"></param>
    /// <param name="uplinkServer">上级地址,用于下载本地不存在的文件</param>
    /// <returns></returns>
    public static void UseFileCache(this IApplicationBuilder app, String requestPath, String localPath, Func<String>? getWhiteIP = null, String? uplinkServer = null)
    {
        var cacheRoot = localPath.GetBasePath().EnsureDirectory(false);
        XTrace.WriteLine("FileCache: {0}", cacheRoot);

        var provider = new CacheFileProvider(cacheRoot, uplinkServer)
        {
            IndexInfoFile = "index.csv",
            Tracer = DefaultTracer.Instance
        };

        if (uplinkServer.IsNullOrEmpty())
        {
            var set = NewLife.Setting.Current;
            provider.GetServers = () => set.PluginServer?.Split(",") ?? new String[0];
        }
        else
        {
            XTrace.WriteLine("UplinkServer: {0}", uplinkServer);
        }

        // IP白名单拦截,必须在使用静态文件前面
        if (getWhiteIP != null)
            app.UseMiddleware<IpFilterMiddleware>(requestPath, getWhiteIP());

        app.UseStaticFiles(new StaticFileOptions
        {
            RequestPath = new PathString(requestPath),
            FileProvider = provider,
            ServeUnknownFileTypes = true,
            DefaultContentType = "application/x-msdownload",
            OnPrepareResponse = ctx => OnPrepareResponse(ctx, getWhiteIP),
        });
        app.UseDirectoryBrowser(new DirectoryBrowserOptions
        {
            RequestPath = new PathString(requestPath),
            FileProvider = provider,
        });
    }

    static void OnPrepareResponse(StaticFileResponseContext ctx, Func<String>? getWhiteIP)
    {
        var ip = ctx.Context.GetUserHost();
        if (ip.IsNullOrEmpty() || !ValidIP(ip, getWhiteIP))
        {
            ctx.Context.Response.StatusCode = (Int32)HttpStatusCode.Forbidden;
            ctx.Context.Response.ContentLength = 0;
            ctx.Context.Response.Body = Stream.Null;
        }
    }

    static Boolean ValidIP(String ip, Func<String>? getWhiteIP)
    {
        if (ip.IsNullOrEmpty()) return false;

        var whites = getWhiteIP?.Invoke();
        if (whites.IsNullOrEmpty()) return true;

        // 白名单里面有的,直接通过
        var ws = (whites + "").Split(",", ";");
        if (ws.Length > 0)
        {
            return ws.Any(e => e.IsMatch(ip));
        }

        // 未设置白名单,黑名单里面没有的,直接通过
        return true;
    }
}