【问题标题】:Get the local path of a file in a virtual directory获取虚拟目录中文件的本地路径
【发布时间】:2021-08-04 08:51:21
【问题描述】:

我有一个 ASP.NET Core 3.0 MVC 应用程序,其中包含图像。例如,

http://foo.bar/images/image.jpg

现在,文件夹images 是一个虚拟目录,它映射到网络驱动器,例如\\192.168.1.1\images

问题:

什么方法将信息/images/image.jpg变成\\192.168.1.1\images\image.jpg?我需要从相对网络路径中检索文件的物理路径

在 ASP.NET Web 窗体中,这可以通过类似 Server.MapPath("~/images/image.jpg") 的方式来完成,但这种方法在 ASP.NET Core 的 HttpContext 中不再存在。

【问题讨论】:

标签: c# asp.net-core iis asp.net-core-3.0


【解决方案1】:

正如 cmets 中的 @Akshay Gaonkar 所指出的,Microsoft 已在 ASP.NET Core (reference) 中明确评估并拒绝了此功能:

我们没有实施此计划的计划。这些概念并没有真正映射到 ASP.NET Core。 URL 本身并不基于任何目录结构。每个组件都有可能映射到目录的约定,但这不是可以概括的。

虽然a workaround is proposed using IFileProvider,它实际上不适用于虚拟目录。但是,您可以做的是建立一个映射服务来转换基本路径,并可选择查询 IIS 以动态检索这些映射,我将在下面讨论。

背景

这一限制源于 ASP.NET Core 不再与 IIS 绑定,而是依赖于抽象层(例如,IWebHostEnvironment)与 Web 服务器通信;由于默认的 ASP.NET Core Kestrel Web 服务器充当反向代理 (reference),这一事实更加复杂:

这会很艰难。我认为我们甚至不可能在当前的反向代理架构中实现。您将不得不维护一个手动映射表。

请记住,虚拟目录(或者更重要的是,虚拟应用程序)的概念对于作为 Web 服务器的 IIS 来说是相当特定的。

解决方法

不幸的是,正如前面摘录中提到的,您唯一真正的选择是在您的虚拟目录与其物理位置之间创建一个映射,然后创建一个为您处理翻译的服务。

以下是关于如何实现这一目标的基本概念验证 - 当然,您可能需要更健壮的产品代码。

界面

这引入了一种抽象,可用于依赖注入和测试目的。我坚持使用 MapPath() 以与旧版 Web 表单签名保持一致。

public interface IVirtualFileProvider
{
    string MapPath(string path);
}

服务

接口的具体实现可能会从a configuration file、数据库——甚至Microsoft Web Administration library 中提取数据。然而,对于这个概念验证,我只是将它们硬编码到提供程序中:

public class VirtualFileProvider: IVirtualFileProvider
{

    // Store dependencies
    private readonly string _webRootPath;

    // Map virtual directories
    private readonly Dictionary<string, string> _virtualDirectories = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
        { "Images", @"\\192.168.1.1\images" }
    };

    public VirtualFileProvider(string webRootPath) {
      _webRootPath = webRootPath;
    }

    public string MapPath(string path)
    {

        // Validate path
        if (String.IsNullOrEmpty(path) || !path.StartsWith("/", StringComparison.Ordinal)) {
            throw new ArgumentException($"The '{path}' should be root relative, and start with a '/'.");
        }

        // Translate path to UNC format
        path                = path.Replace("/", @"\", StringComparison.Ordinal);

        // Isolate first folder (or file)
        var firstFolder     = path.IndexOf(@"\", 1);
        if (firstFolder < 0)
        {
            firstFolder     = path.Length;
        }

        // Parse root directory from remainder of path
        var rootDirectory   = path.Substring(1, firstFolder-1);
        var relativePath    = path.Substring(firstFolder);

        // Return virtual directory
        if (_virtualDirectories.ContainsKey(rootDirectory))
        {
            return _virtualDirectories[rootDirectory] + relativePath;
        }

        // Return non-virtual directory
        return _webRootPath + @"\" + rootDirectory + relativePath;

    }

}

注册

实现需要了解默认 Web 根目录,以便为不在虚拟目录中的文件转换路径。这可以动态检索,如@Pashyant Srivastava's answer 所示,尽管我在这里使用IWebHostEnvironment。这样,您可以将VirtualFileProvider 注册为使用 ASP.NET Core 的依赖注入容器的单例生活方式:

public class Startup 
{

    private readonly IWebHostEnvironment _hostingEnvironment;

    public Startup(IWebHostEnvironment webHostEnvironment) 
    {
        _hostingEnvironment = webHostEnvironment;
    }

    public void ConfigureServices(IServiceCollection services)
    {

        // Add framework services.
        services.AddMvc();

        // Register virtual file provider
        services.AddSingleton<IVirtualFileProvider>(new VirtualFileProvider(_hostingEnvironment.WebRootPath));

    }

    public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) 
    {
        …
    }

}

实施

注册实现后,您可以将提供程序注入到 MVC 控制器的构造函数中,甚至可以直接注入到您的操作中:

public IActionResult MyAction([FromServices] IVirtualFileProvider fileProvider, string file)
    => Content(fileProvider?.MapPath(file));

限制

上面的代码没有努力验证文件实际上是否存在——尽管很容易通过File.Exists()添加。这显然会使通话费用更高。

动态映射

上述实现依赖于硬编码值。不过,如前所述,Microsoft Web Administration library 提供了以编程方式与 IIS 交互的方法。这包括用于从 IIS 中提取虚拟目录列表的 Application.VirtualDirectories property

var directories = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var manager     = new ServerManager();
var site        = manager.Sites["Default Web Site"];
var application = site[0]; 
                
foreach (var virtualDirectory in application.VirtualDirectories)
{
    directories.Add(virtualDirectory.Path, virtualDirectory.PhysicalPath);
}

这可以与VirtualFileProvider 集成,以便在需要时动态评估可用的虚拟目录。

警告: Microsoft Web 管理库尚未更新以支持 .NET 5,并维护对不向前兼容的 .NET Core 3.x 库的依赖。目前尚不清楚微软何时或是否会发布 .NET 5 兼容版本。由于您的问题特定于 .NET Core 3.1,因此这可能不是一个直接的问题。但由于 .NET 5 是 .NET 的当前版本,因此引入对 Microsoft Web 管理库的依赖可能会带来长期风险。

结论

我知道这不是您希望的方法。但是,根据您的具体实施,这可能是一个可接受的解决方法。显然,如果这是一个可重用的库,它被放置在您不了解虚拟目录的各种站点上,您需要将数据与实现分开。不过,这至少提供了一个可以使用的基本结构。

【讨论】:

    【解决方案2】:

    您可以从 IHostingEnvironment 依赖项中获取此信息。这将由 ASP.NET Core 框架填充,然后您可以获取当前 web 目录的值。

    private readonly IHostingEnvironment _hostingEnvironment;
    
    public EmployeeController(IHostingEnvironment hostingEnvironment)
    {
        _hostingEnvironment = hostingEnvironment;
    }
    
    // Get the path to write
    string webRootPath = _hostingEnvironment.WebRootPath;
    
    // ASP.NET Core application root directory under the wwwroot directory
     
    // Get static directory
    string contentRootPath = _hostingEnvironment.ContentRootPath;
    // The web root directory refers to the root directory that provides static content; there is no wwwroot.
    
    // Through the path and then through the IO stream to determine whether the file you are passing in the directory exists
    DirectoryInfo directoryInfo = new DirectoryInfo(webRootPath+"/uploads/images/");
    

    【讨论】:

    • 这不是获取根路径,而是获取虚拟目录的物理路径,可以在任何地方
    【解决方案3】:

    您可以首先将虚拟路径(网络驱动器)映射到您的本地设备并使用PhysicalFileProvider。更详细的用例见here

    app.UseFileServer(new FileServerOptions
            {
                IFileProvider provider = new PhysicalFileProvider(@"\\server\path"),
                RequestPath = new PathString("/MyPath"),
                EnableDirectoryBrowsing = false
            });
    

    【讨论】:

    • 这假设预先知道用户在虚拟目录中,以及该目录映射到什么。我的假设是 OP 在现实世界的场景中不一定知道这一点,并且希望代码能够根据相对 Web 路径之外的任何信息来确定正确的物理路径。
    • @JeremyCaney 完全正确
    • 您确实有一个有趣的观点:虚拟路径怎么可能是动态的?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-18
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多