MVC4 StyleBundle不解析图像
我的问题与此类似:
ASP.NET MVC 4缩小和背景图片
除非我想坚持使用MVC自己的捆绑包。 我正在脑部崩溃,试图找出指定样式包的正确模式,以使独立的css和图像集(如jQuery UI)能够工作。
我有一个典型的MVC网站结构/Content/css/
其中包含我的基础CSS,如styles.css
。 在该css文件夹中,我还有一些子文件夹,例如/jquery-ui
,其中包含其CSS文件和一个/images
文件夹。 jQuery UI CSS中的图像路径与该文件夹相关,我不想惹他们。
据我了解,当我指定一个StyleBundle
我需要指定一个虚拟路径,它不与实际的内容路径匹配,因为(假设我忽略了到内容的路由),IIS会尝试将该路径解析为一个物理文件。 所以我指定:
bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
.Include("~/Content/css/jquery-ui/*.css"));
呈现使用:
@Styles.Render("~/Content/styles/jquery-ui")
我可以看到要求:
http://localhost/MySite/Content/styles/jquery-ui?v=nL_6HPFtzoqrts9nwrtjq0VQFYnhMjY5EopXsK8cxmg1
这是返回正确的,缩小的CSS响应。 但是,然后浏览器发送一个相对链接图像的请求,如下所示:
http://localhost/MySite/Content/styles/images/ui-bg_highlight-soft_100_eeeeee_1x100.png
这是一个404
。
我知道我的URL的最后一部分jquery-ui
是一个无扩展名的URL,是我的包的处理程序,所以我可以明白为什么图像的相对请求只是/styles/images/
。
所以我的问题是处理这种情况的正确方法是什么 ?
根据MVC4 CSS捆绑和图像引用的这个线程,如果你定义你的bundle为:
bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
.Include("~/Content/css/jquery-ui/*.css"));
如果您在与构成捆绑软件的源文件相同的路径上定义捆绑软件,那么相对的图像路径仍然可以工作。 包路径的最后一部分实际上是该特定包的file name
(即, /bundle
可以是任何你喜欢的名称)。
如果你将同一个文件夹中的CSS捆绑在一起(我认为从捆绑的角度来看是有意义的),这只会起作用。
更新
根据以下@Hao Kung的评论,现在可以通过应用CssRewriteUrlTransformation
(绑定时更改相对于CSS文件的URL引用)来实现。
注意:我没有确认有关在虚拟目录中重写为绝对路径的问题的意见,所以这可能不适用于所有人(?)。
bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
.Include("~/Content/css/jquery-ui/*.css",
new CssRewriteUrlTransform()));
格林/ ThePirat解决方案运作良好。
我不喜欢它在包上添加了Include方法,并且它在内容目录中创建了临时文件。 (他们最终得到检查,部署,然后服务无法启动!)
所以要遵循Bundling的设计,我选择执行基本相同的代码,但在IBundleTransform实现中::
class StyleRelativePathTransform
: IBundleTransform
{
public StyleRelativePathTransform()
{
}
public void Process(BundleContext context, BundleResponse response)
{
response.Content = String.Empty;
Regex pattern = new Regex(@"urls*(s*([""']?)([^:)]+)1s*)", RegexOptions.IgnoreCase);
// open each of the files
foreach (FileInfo cssFileInfo in response.Files)
{
if (cssFileInfo.Exists)
{
// apply the RegEx to the file (to change relative paths)
string contents = File.ReadAllText(cssFileInfo.FullName);
MatchCollection matches = pattern.Matches(contents);
// Ignore the file if no match
if (matches.Count > 0)
{
string cssFilePath = cssFileInfo.DirectoryName;
string cssVirtualPath = context.HttpContext.RelativeFromAbsolutePath(cssFilePath);
foreach (Match match in matches)
{
// this is a path that is relative to the CSS file
string relativeToCSS = match.Groups[2].Value;
// combine the relative path to the cssAbsolute
string absoluteToUrl = Path.GetFullPath(Path.Combine(cssFilePath, relativeToCSS));
// make this server relative
string serverRelativeUrl = context.HttpContext.RelativeFromAbsolutePath(absoluteToUrl);
string quote = match.Groups[1].Value;
string replace = String.Format("url({0}{1}{0})", quote, serverRelativeUrl);
contents = contents.Replace(match.Groups[0].Value, replace);
}
}
// copy the result into the response.
response.Content = String.Format("{0}rn{1}", response.Content, contents);
}
}
}
}
然后将它包裹在Bundle Implemetation中:
public class StyleImagePathBundle
: Bundle
{
public StyleImagePathBundle(string virtualPath)
: base(virtualPath)
{
base.Transforms.Add(new StyleRelativePathTransform());
base.Transforms.Add(new CssMinify());
}
public StyleImagePathBundle(string virtualPath, string cdnPath)
: base(virtualPath, cdnPath)
{
base.Transforms.Add(new StyleRelativePathTransform());
base.Transforms.Add(new CssMinify());
}
}
样本用法:
static void RegisterBundles(BundleCollection bundles)
{
...
bundles.Add(new StyleImagePathBundle("~/bundles/Bootstrap")
.Include(
"~/Content/css/bootstrap.css",
"~/Content/css/bootstrap-responsive.css",
"~/Content/css/jquery.fancybox.css",
"~/Content/css/style.css",
"~/Content/css/error.css",
"~/Content/validation.css"
));
这里是我的RelativeFromAbsolutePath的扩展方法:
public static string RelativeFromAbsolutePath(this HttpContextBase context, string path)
{
var request = context.Request;
var applicationPath = request.PhysicalApplicationPath;
var virtualDir = request.ApplicationPath;
virtualDir = virtualDir == "/" ? virtualDir : (virtualDir + "/");
return path.Replace(applicationPath, virtualDir).Replace(@"", "/");
}
更好的是(恕我直言)实施一个自定义的束修复图像路径。 我为我的应用程序写了一个。
using System;
using System.Collections.Generic;
using IO = System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Optimization;
...
public class StyleImagePathBundle : Bundle
{
public StyleImagePathBundle(string virtualPath)
: base(virtualPath, new IBundleTransform[1]
{
(IBundleTransform) new CssMinify()
})
{
}
public StyleImagePathBundle(string virtualPath, string cdnPath)
: base(virtualPath, cdnPath, new IBundleTransform[1]
{
(IBundleTransform) new CssMinify()
})
{
}
public new Bundle Include(params string[] virtualPaths)
{
if (HttpContext.Current.IsDebuggingEnabled)
{
// Debugging. Bundling will not occur so act normal and no one gets hurt.
base.Include(virtualPaths.ToArray());
return this;
}
// In production mode so CSS will be bundled. Correct image paths.
var bundlePaths = new List<string>();
var svr = HttpContext.Current.Server;
foreach (var path in virtualPaths)
{
var pattern = new Regex(@"urls*(s*([""']?)([^:)]+)1s*)", RegexOptions.IgnoreCase);
var contents = IO.File.ReadAllText(svr.MapPath(path));
if(!pattern.IsMatch(contents))
{
bundlePaths.Add(path);
continue;
}
var bundlePath = (IO.Path.GetDirectoryName(path) ?? string.Empty).Replace(@"", "/") + "/";
var bundleUrlPath = VirtualPathUtility.ToAbsolute(bundlePath);
var bundleFilePath = String.Format("{0}{1}.bundle{2}",
bundlePath,
IO.Path.GetFileNameWithoutExtension(path),
IO.Path.GetExtension(path));
contents = pattern.Replace(contents, "url($1" + bundleUrlPath + "$2$1)");
IO.File.WriteAllText(svr.MapPath(bundleFilePath), contents);
bundlePaths.Add(bundleFilePath);
}
base.Include(bundlePaths.ToArray());
return this;
}
}
要使用它,请执行:
bundles.Add(new StyleImagePathBundle("~/bundles/css").Include(
"~/This/Is/Some/Folder/Path/layout.css"));
...代替...
bundles.Add(new StyleBundle("~/bundles/css").Include(
"~/This/Is/Some/Folder/Path/layout.css"));
它所做的是(当不在调试模式下)查找url(<something>)
并用url(<absolutepathtosomething>)
替换它。 我在10秒前写了这个东西,所以可能需要稍微调整一下。 我通过确保URL路径中没有冒号(:)来考虑完全限定的URL和base64 DataURI。 在我们的环境中,图像通常与其css文件驻留在同一个文件夹中,但我已经用父文件夹( url(../someFile.png)
)和子文件夹( url(someFolder/someFile.png
)) url(someFolder/someFile.png
。