这个问题很老,但问题仍然存在,虽然 Artak 提供的解决方案有效,但在大多数情况下它在概念上是不正确的。首先让我们看看问题的根源:
asp-append-version
查找IHostingEnvironment.WebRootFileProvider
默认情况下使用PhysicalFileProvider
指向该wwwroot
文件夹的文件。
核心文档有一个关于如何在 web 根目录之外提供文件的示例:
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles(); // For the wwwroot folder
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = "/StaticFiles"
});
}
wwwroot
这允许您从和文件夹中提供静态文件MyStaticFiles
。如果您有图像\MyStaticFiles\pic1.jpg
,可以通过两种方式引用它:
<img src="~/pic1.jpg" />
<img src="~/StaticFiles/pic1.jpg" />
两者都将同样有效。这在概念上是不正确的,因为您为路径指定了别名/StaticFiles
,因此不应将其文件与 root 组合/
。但至少它有效,它给你你想要的。
可悲的是,asp-append-version
不知道这一切。它应该,但它没有。应该是因为它旨在与静态文件(JavaScript、CSS 和图像)一起使用,所以如果我们更改配置以提供来自不同文件夹的静态文件,则asp-append-version
可以获得这些配置的副本。它没有,所以我们需要通过 modify 单独配置它IHostingEnvironment.WebRootFileProvider
。
Artak 建议使用CompositeFileProvider
它允许我们将多个文件提供程序分配给IHostingEnvironment.WebRootFileProvider
. 这确实有效,但它有一个基本问题。CompositeFileProvider
不允许我们RequestPath
在StaticFileOptions
. 作为一种解决方法,Artak 建议我们不应该使用前缀,它利用了上述错误行为,即文件可以以两种方式引用。为了演示这个问题,假设另一个文件夹的结构如下:
|_ 我的静态文件
|_ HTML
| |_ 隐私.html
| |_ 常见问题.html
|_ 图片
|_ 图像1.jpg
现在,文件MyStaticFiles\images
夹中的所有文件会发生什么?假设它wwwroot
也有images
文件夹,它会工作还是给你两个同名文件夹的错误?文件~/images/image1.jpg
将从哪里来?
无论它是否有效,通常有一个重要原因导致您将静态文件放在wwwroot
. 这通常是因为这些静态文件是您不希望与网站设计文件混合的内容文件。
我们需要一个允许我们RequestPath
为每个文件夹指定的提供程序。由于 Core 目前没有这样的提供者,我们只能选择自己编写。虽然不难,但这不是许多程序员喜欢处理的任务。这是一个快速实现,它并不完美,但它可以完成工作。它基于Marius Zkochanowski 提供的示例,并进行了一些改进:
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Primitives;
namespace Microsoft.Extensions.FileProviders {
class CompositeFileWithOptionsProvider : IFileProvider {
private readonly IFileProvider _webRootFileProvider;
private readonly IEnumerable<StaticFileOptions> _staticFileOptions;
public CompositeFileWithOptionsProvider(IFileProvider webRootFileProvider, params StaticFileOptions[] staticFileOptions)
: this(webRootFileProvider, (IEnumerable<StaticFileOptions>)staticFileOptions) { }
public CompositeFileWithOptionsProvider(IFileProvider webRootFileProvider, IEnumerable<StaticFileOptions> staticFileOptions) {
_webRootFileProvider = webRootFileProvider ?? throw new ArgumentNullException(nameof(webRootFileProvider));
_staticFileOptions = staticFileOptions;
}
public IDirectoryContents GetDirectoryContents(string subpath) {
var provider = GetFileProvider(subpath, out string outpath);
return provider.GetDirectoryContents(outpath);
}
public IFileInfo GetFileInfo(string subpath) {
var provider = GetFileProvider(subpath, out string outpath);
return provider.GetFileInfo(outpath);
}
public IChangeToken Watch(string filter) {
var provider = GetFileProvider(filter, out string outpath);
return provider.Watch(outpath);
}
private IFileProvider GetFileProvider(string path, out string outpath) {
outpath = path;
var fileProviders = _staticFileOptions;
if (fileProviders != null) {
foreach (var item in fileProviders) {
if (path.StartsWith(item.RequestPath, StringComparison.Ordinal)) {
outpath = path.Substring(item.RequestPath.Value.Length, path.Length - item.RequestPath.Value.Length);
return item.FileProvider;
}
}
}
return _webRootFileProvider;
}
}
}
现在我们可以更新 Artak 的示例以使用新的提供程序:
app.UseStaticFiles(); //For the wwwroot folder.
//This serves static files from the given folder similar to IIS virtual directory.
var options = new StaticFileOptions {
FileProvider = new PhysicalFileProvider(Configuration.GetValue<string>("ContentPath")),
RequestPath = "/Content"
};
//This is required for asp-append-version (it needs to know where to find the file to hash it).
env.WebRootFileProvider = new CompositeFileWithOptionsProvider(env.WebRootFileProvider, options);
app.UseStaticFiles(options); //For any folders other than wwwroot.
在这里,我从配置文件中获取路径,因为它通常甚至完全位于应用程序的文件夹之外。/Content
现在您可以使用而不是引用您的内容文件~/
。例子:
<img src="~/Content/images/pic1.jpg" asp-append-version="true" />