[英]Get the local path of a file in a virtual directory
我有一个 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
? 我需要从相对 web 路径中检索文件的物理路径。
In ASP.NET Web Forms, this could be done by something like Server.MapPath("~/images/image.jpg")
, but this method doesn't exist in ASP.NET Core's HttpContext
anymore.
正如@Akshay Gaonkar在评论中指出的那样,微软已经明确评估并拒绝了 ASP.NET Core 中的此功能(参考):
我们没有计划实施这一点。 ASP.NET 核心中的概念并不是真正的 map。 URL 本身并不基于任何目录结构。 每个组件都有可能 map 到目录的约定,但这不是可以概括的东西。
虽然提出了使用IFileProvider
的解决方法,但它实际上不适用于虚拟目录。 但是,您可以做的是建立一个映射服务来转换基本路径,并可选择查询 IIS 以动态检索这些映射,我将在下面讨论。
此限制源于 ASP.NET 内核不再与 IIS 相关联,而是依赖于抽象层(例如IWebHostEnvironment
)与 Z2567A5EC9705EB7AC2C984033E061 服务器通信; 由于默认的 ASP.NET Core Kestrel web 服务器充当反向代理( 参考),这一事实更加复杂:
那会很艰难。 我认为我们甚至不可能在当前的反向代理架构中实现。 您将不得不维护一个手动映射表。
请记住,虚拟目录(或者更重要的是,虚拟应用程序)的概念对于 IIS 作为 web 服务器是相当特定的。
不幸的是,正如前面摘录中提到的,您唯一真正的选择是在您的虚拟目录和它们的物理位置之间创建一个映射,然后创建一个为您处理翻译的服务。
下面是一个基本的概念证明,说明您如何实现这一目标——当然,您可能需要更健壮的产品代码。
这引入了一种抽象,可用于依赖注入和测试目的。 为了与旧版 Web Forms 签名保持一致,我坚持使用MapPath()
。
public interface IVirtualFileProvider
{
string MapPath(string path);
}
接口的具体实现可能会从配置文件、数据库甚至是Microsoft Web 管理库中提取数据。 但是,对于这个概念验证,我只是将它们硬编码到提供程序中:
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 的回答所示,尽管我在这里使用IWebHostEnvironment
。 这样,您可以使用 ASP.NET Core 的依赖注入容器将VirtualFileProvider
注册为 singleton 生活方式:
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 管理库提供了以编程方式与 IIS 交互的方法。 这包括用于从 IIS 中提取虚拟目录列表的Application.VirtualDirectories
属性:
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 管理库的依赖可能会带来长期风险。
我知道这不是您希望的方法。 但是,根据您的具体实施,这可能是一个可接受的解决方法。 显然,如果这是一个可重用的库,它被放置在您不了解虚拟目录的各种站点上,您需要将数据与实现分开。 不过,这至少提供了一个可以使用的基本结构。
您可以从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/");
您可以首先将虚拟路径(网络驱动器)映射到本地设备并使用PhysicalFileProvider 。 在这里找到了更详细的用例
app.UseFileServer(new FileServerOptions
{
IFileProvider provider = new PhysicalFileProvider(@"\\server\path"),
RequestPath = new PathString("/MyPath"),
EnableDirectoryBrowsing = false
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.