获取虚拟目录中文件的本地路径

时间:2021-05-14 06:28:34

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

我有一个 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 Forms 中,这可以通过类似 Server.MapPath("~/images/image.jpg") 的东西来完成,但这种方法在 ASP.NET Core 的 HttpContext 中不再存在。

3 个答案:

答案 0 :(得分:1)

正如 @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 来说是相当特定的。

解决方法

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

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

界面

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

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。这样,您就可以使用 ASP.NET Core 的依赖注入容器将 VirtualFileProvider 注册为单例生活方式:

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 管理库的依赖可能会带来长期风险。

结论

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

答案 1 :(得分:0)

您可以从 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/");

答案 2 :(得分:0)

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

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