CSS MVC4 StyleBundle 无法解析图像
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11355935/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MVC4 StyleBundle not resolving images
提问by Tom W Hall
My question is similar to this:
我的问题与此类似:
Except that I want to stick with MVC's own bundling if I can. I'm having a brain crash trying to figure out what the correct pattern is for specifying style bundles such that standalone css and image sets such as jQuery UI work.
除了我想尽可能坚持使用 MVC 自己的捆绑。我在试图找出指定样式包的正确模式是什么,以便独立的 css 和图像集(如 jQuery UI)工作时,我脑子崩溃了。
I have a typical MVC site structure with /Content/css/
which contains my base CSS such as styles.css
. Within that css folder I also have subfolders such as /jquery-ui
which contains its CSS file plus an /images
folder. Image paths in the jQuery UI CSS are relative to that folder and I don't want to mess with them.
我有一个典型的 MVC 站点结构,/Content/css/
其中包含我的基本 CSS,例如styles.css
. 在那个 css 文件夹中,我还有子文件夹,例如/jquery-ui
包含它的 CSS 文件和一个/images
文件夹。jQuery UI CSS 中的图像路径是相对于该文件夹的,我不想弄乱它们。
As I understand it, when I specify a StyleBundle
I need to specify a virtual path which does not also match a real content path, because (assuming I'm ignoring routes to Content) IIS would then try to resolve that path as a physical file. So I'm specifying:
据我了解,当我指定 a 时,StyleBundle
我需要指定一个与真实内容路径不匹配的虚拟路径,因为(假设我忽略了内容的路由)IIS 会尝试将该路径解析为物理文件。所以我指定:
bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
.Include("~/Content/css/jquery-ui/*.css"));
rendered using:
渲染使用:
@Styles.Render("~/Content/styles/jquery-ui")
I can see the request going out to:
我可以看到请求发送到:
http://localhost/MySite/Content/styles/jquery-ui?v=nL_6HPFtzoqrts9nwrtjq0VQFYnhMjY5EopXsK8cxmg1
This is returning the correct, minified CSS response. But then the browser sends a request for a relatively linked image as:
这将返回正确的、缩小的 CSS 响应。但随后浏览器发送了一个相对链接图像的请求,如下所示:
http://localhost/MySite/Content/styles/images/ui-bg_highlight-soft_100_eeeeee_1x100.png
Which is a 404
.
这是一个404
.
I understand that the last part of my URL jquery-ui
is an extensionless URL, a handler for my bundle, so I can see why the relative request for the image is simply /styles/images/
.
我知道我的 URL 的最后一部分jquery-ui
是一个无扩展名的 URL,我的包的处理程序,所以我可以理解为什么对图像的相对请求只是/styles/images/
.
So my question is what is the correct wayof handling this situation?
所以我的问题是处理这种情况的正确方法是什么?
回答by Chris Baxter
According to this thread on MVC4 css bundling and image references, if you define your bundle as:
根据MVC4 cssbundle and image references上的这个线程,如果您将包定义为:
bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
.Include("~/Content/css/jquery-ui/*.css"));
Where you define the bundle on the same path as the source files that made up the bundle, the relative image paths will still work. The last part of the bundle path is really the file name
for that specific bundle (i.e., /bundle
can be any name you like).
如果您在与构成包的源文件相同的路径上定义包,相对图像路径仍然有效。包路径的最后一部分实际上是file name
该特定包的名称(即,/bundle
可以是您喜欢的任何名称)。
This will only work if you are bundling together CSS from the same folder (which I think makes sense from a bundling perspective).
这仅在您将来自同一文件夹的 CSS 捆绑在一起时才有效(我认为从捆绑的角度来看这是有道理的)。
Update
更新
As per the comment below by @Hao Kung, alternatively this may now be achieved by applying a CssRewriteUrlTransformation
(Change relative URL references to CSS files when bundled).
根据@Hao Kung 在下面的评论,或者现在可以通过应用CssRewriteUrlTransformation
(捆绑时更改对 CSS 文件的相对 URL 引用 ) 来实现。
NOTE: I have not confirmed comments regarding issues with rewriting to absolute paths within a virtual directory, so this may not work for everyone (?).
注意:我尚未确认有关重写虚拟目录中的绝对路径问题的评论,因此这可能不适用于所有人 (?)。
bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
.Include("~/Content/css/jquery-ui/*.css",
new CssRewriteUrlTransform()));
回答by AcidPAT
Grinn / ThePirat solution works well.
Grinn / ThePirat 解决方案运行良好。
I did not like that it new'd the Include method on bundle, and that it created temporary files in the content directory. (they ended up getting checked in, deployed, then the service wouldn't start!)
我不喜欢它在 bundle 上新增了 Include 方法,并且它在内容目录中创建了临时文件。(他们最终被签入、部署,然后服务无法启动!)
So to follow the design of Bundling, I elected to perform essentially the same code, but in an IBundleTransform implementation::
因此,为了遵循 Bundling 的设计,我选择执行本质上相同的代码,但在 IBundleTransform 实现中:
class StyleRelativePathTransform
: IBundleTransform
{
public StyleRelativePathTransform()
{
}
public void Process(BundleContext context, BundleResponse response)
{
response.Content = String.Empty;
Regex pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\s*\)", 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}\r\n{1}", response.Content, contents);
}
}
}
}
And then wrapped this up in a Bundle Implemetation:
然后将其包装在 Bundle 实现中:
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());
}
}
Sample Usage:
示例用法:
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"
));
Here is my extension method for RelativeFromAbsolutePath:
这是我对 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(@"\", "/");
}
回答by Grinn
Better yet (IMHO) implement a custom Bundle that fixes the image paths. I wrote one for my app.
更好的是(恕我直言)实现修复图像路径的自定义捆绑包。我为我的应用程序写了一个。
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(@"url\s*\(\s*([""']?)([^:)]+)\s*\)", 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(" + bundleUrlPath + ")");
IO.File.WriteAllText(svr.MapPath(bundleFilePath), contents);
bundlePaths.Add(bundleFilePath);
}
base.Include(bundlePaths.ToArray());
return this;
}
}
To use it, do:
要使用它,请执行以下操作:
bundles.Add(new StyleImagePathBundle("~/bundles/css").Include(
"~/This/Is/Some/Folder/Path/layout.css"));
...instead of...
...代替...
bundles.Add(new StyleBundle("~/bundles/css").Include(
"~/This/Is/Some/Folder/Path/layout.css"));
What it does is (when not in debug mode) looks for url(<something>)
and replaces it with url(<absolute\path\to\something>)
. I wrote the thing about 10 seconds ago so it might need a little tweaking. I've taken into account fully-qualified URLs and base64 DataURIs by making sure there's no colons (:) in the URL path. In our environment, images normally reside in the same folder as their css files, but I've tested it with both parent folders (url(../someFile.png)
) and child folders (url(someFolder/someFile.png
).
它的作用是(当不处于调试模式时)查找url(<something>)
并将其替换为url(<absolute\path\to\something>)
. 我大约 10 秒前写了这个东西,所以它可能需要一些调整。我通过确保 URL 路径中没有冒号 (:) 来考虑完全限定的 URL 和 base64 DataURI。在我们的环境中,图像通常与它们的 css 文件位于同一文件夹中,但我已经使用父文件夹 ( url(../someFile.png)
) 和子文件夹 ( url(someFolder/someFile.png
) 对其进行了测试。
回答by Tony Wall
It is not necessary to specify a transform or have crazy subdirectory paths. After much troubleshooting I isolated it to this "simple" rule (is it a bug?)...
没有必要指定转换或拥有疯狂的子目录路径。经过多次故障排除后,我将其隔离到这个“简单”规则(这是一个错误吗?)...
If your bundle path does not start with relative root of the items being included, then the web application root will not be taken into account.
如果您的包路径不以所包含项目的相对根目录开头,则不会考虑 Web 应用程序根目录。
Sounds like more of a bug to me, but anyway that's how you fix it with the current .NET 4.51 version. Perhaps the other answers were necessary on older ASP.NET builds, can't say don't have time to retrospectively test all that.
对我来说听起来更像是一个错误,但无论如何,这就是您使用当前 .NET 4.51 版本修复它的方式。也许其他答案在较旧的 ASP.NET 版本上是必要的,不能说没有时间回顾性测试所有这些。
To clarify, here is an example:
为了澄清,这里有一个例子:
I have these files...
我有这些文件...
~/Content/Images/Backgrounds/Some_Background_Tile.gif
~/Content/Site.css - references the background image relatively, i.e. background: url('Images/...')
Then setup the bundle like...
然后设置捆绑包,如...
BundleTable.Add(new StyleBundle("~/Bundles/Styles").Include("~/Content/Site.css"));
And render it like...
并将其渲染为...
@Styles.Render("~/Bundles/Styles")
And get the "behaviour" (bug), the CSS files themselves have the application root (e.g. "http://localhost:1234/MySite/Content/Site.css") but the CSS image within all start "/Content/Images/..." or "/Images/..." depending on whether I add the transform or not.
并获得“行为”(错误),CSS 文件本身具有应用程序根目录(例如“http://localhost:1234/MySite/Content/Site.css”),但所有的 CSS 图像都以“/Content/Images”开头/..." 或 "/Images/..." 取决于我是否添加转换。
Even tried creating the "Bundles" folder to see if it was to do with the path existing or not, but that didn't change anything. The solution to the problem is really the requirement that the name of the bundle must start with the path root.
甚至尝试创建“Bundles”文件夹以查看它是否与现有路径有关,但这并没有改变任何东西。问题的解决方案实际上是要求捆绑包的名称必须以路径根开头。
Meaning this example is fixed by registering and rendering the bundle path like..
这意味着这个例子是通过注册和渲染包路径来修复的,比如..
BundleTable.Add(new StyleBundle("~/Content/StylesBundle").Include("~/Content/Site.css"));
...
@Styles.Render("~/Content/StylesBundle")
So of course you could say this is RTFM, but I am quite sure me and others picked-up this "~/Bundles/..." path from the default template or somewhere in documentation at MSDN or ASP.NET web site, or just stumbled upon it because actually it's a quite logical name for a virtual path and makes sense to choose such virtual paths which do not conflict with real directories.
所以当然你可以说这是 RTFM,但我很确定我和其他人从默认模板或 MSDN 或 ASP.NET 网站的文档中的某个地方选择了这个“~/Bundles/...”路径,或者只是偶然发现它,因为实际上它是虚拟路径的一个非常合乎逻辑的名称,并且选择与实际目录不冲突的此类虚拟路径是有意义的。
Anyway, that's the way it is. Microsoft see no bug. I don't agree with this, either it should work as expected or some exception should be thrown, or an additional override to adding the bundle path which opts to include the application root or not. I can't imagine why anyone would not want the application root included when there was one (normally unless you installed your web site with a DNS alias/default web site root). So actually that should be the default anyway.
无论如何,事情就是这样。微软看不到错误。我不同意这一点,要么它应该按预期工作,要么应该抛出一些异常,或者额外覆盖添加选择是否包含应用程序根目录的包路径。我无法想象为什么有人不希望包含应用程序根目录(通常除非您使用 DNS 别名/默认网站根目录安装您的网站)。所以实际上这应该是默认值。
回答by ajbeaven
I found that CssRewriteUrlTransform fails to run if you're referencing a *.css
file and you have the associated *.min.css
file in the same folder.
我发现 CssRewriteUrlTransform 无法运行,如果您正在引用一个*.css
文件并且您*.min.css
在同一文件夹中有关联的文件。
To fix this, either delete the *.min.css
file or reference it directly in your bundle:
要解决此问题,请删除该*.min.css
文件或直接在您的包中引用它:
bundles.Add(new Bundle("~/bundles/bootstrap")
.Include("~/Libs/bootstrap3/css/bootstrap.min.css", new CssRewriteUrlTransform()));
After that you do that, your URLs will be transformed correctly and your images should be correctly resolved.
之后,您的 URL 将被正确转换,并且您的图像应该被正确解析。
回答by SimonGates
Maybe I am biased, but I quite like my solution as it doesn't do any transforming, regex's etc and it's has the least amount of code :)
也许我有偏见,但我非常喜欢我的解决方案,因为它不进行任何转换、正则表达式等,而且代码量最少:)
This works for a site hosted as a Virtual Directory in a IIS Web Site and as a root website on IIS
这适用于作为IIS 网站中的虚拟目录和 IIS 上的根网站托管的站点
So I created an Implentation of IItemTransform
encapsulated the CssRewriteUrlTransform
and used VirtualPathUtility
to fix the path and call the existing code:
所以我创建了一个IItemTransform
封装的Implentation,CssRewriteUrlTransform
用于VirtualPathUtility
修复路径和调用现有代码:
/// <summary>
/// Is a wrapper class over CssRewriteUrlTransform to fix url's in css files for sites on IIS within Virutal Directories
/// and sites at the Root level
/// </summary>
public class CssUrlTransformWrapper : IItemTransform
{
private readonly CssRewriteUrlTransform _cssRewriteUrlTransform;
public CssUrlTransformWrapper()
{
_cssRewriteUrlTransform = new CssRewriteUrlTransform();
}
public string Process(string includedVirtualPath, string input)
{
return _cssRewriteUrlTransform.Process("~" + VirtualPathUtility.ToAbsolute(includedVirtualPath), input);
}
}
//App_Start.cs
public static void Start()
{
BundleTable.Bundles.Add(new StyleBundle("~/bundles/fontawesome")
.Include("~/content/font-awesome.css", new CssUrlTransformWrapper()));
}
Seems to work fine for me?
对我来说似乎工作正常?
回答by nrodic
Although Chris Baxter's answer helps with original problem, it doesn't work in my case when application is hosted in virtual directory. After investigating the options, I finished with DIY solution.
尽管 Chris Baxter 的回答有助于解决原始问题,但当应用程序托管在虚拟目录中时,它在我的情况下不起作用。在调查了选项后,我完成了 DIY 解决方案。
ProperStyleBundle
class includes code borrowed from original CssRewriteUrlTransform
to properly transform relative paths within virtual directory. It also throws if file doesn't exist and prevents reordering of files in the bundle (code taken from BetterStyleBundle
).
ProperStyleBundle
类包括从原始借来的代码,CssRewriteUrlTransform
以正确转换虚拟目录中的相对路径。如果文件不存在,它也会抛出并阻止重新排序包中的文件(代码取自BetterStyleBundle
)。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Optimization;
using System.Linq;
namespace MyNamespace
{
public class ProperStyleBundle : StyleBundle
{
public override IBundleOrderer Orderer
{
get { return new NonOrderingBundleOrderer(); }
set { throw new Exception( "Unable to override Non-Ordered bundler" ); }
}
public ProperStyleBundle( string virtualPath ) : base( virtualPath ) {}
public ProperStyleBundle( string virtualPath, string cdnPath ) : base( virtualPath, cdnPath ) {}
public override Bundle Include( params string[] virtualPaths )
{
foreach ( var virtualPath in virtualPaths ) {
this.Include( virtualPath );
}
return this;
}
public override Bundle Include( string virtualPath, params IItemTransform[] transforms )
{
var realPath = System.Web.Hosting.HostingEnvironment.MapPath( virtualPath );
if( !File.Exists( realPath ) )
{
throw new FileNotFoundException( "Virtual path not found: " + virtualPath );
}
var trans = new List<IItemTransform>( transforms ).Union( new[] { new ProperCssRewriteUrlTransform( virtualPath ) } ).ToArray();
return base.Include( virtualPath, trans );
}
// This provides files in the same order as they have been added.
private class NonOrderingBundleOrderer : IBundleOrderer
{
public IEnumerable<BundleFile> OrderFiles( BundleContext context, IEnumerable<BundleFile> files )
{
return files;
}
}
private class ProperCssRewriteUrlTransform : IItemTransform
{
private readonly string _basePath;
public ProperCssRewriteUrlTransform( string basePath )
{
_basePath = basePath.EndsWith( "/" ) ? basePath : VirtualPathUtility.GetDirectory( basePath );
}
public string Process( string includedVirtualPath, string input )
{
if ( includedVirtualPath == null ) {
throw new ArgumentNullException( "includedVirtualPath" );
}
return ConvertUrlsToAbsolute( _basePath, input );
}
private static string RebaseUrlToAbsolute( string baseUrl, string url )
{
if ( string.IsNullOrWhiteSpace( url )
|| string.IsNullOrWhiteSpace( baseUrl )
|| url.StartsWith( "/", StringComparison.OrdinalIgnoreCase )
|| url.StartsWith( "data:", StringComparison.OrdinalIgnoreCase )
) {
return url;
}
if ( !baseUrl.EndsWith( "/", StringComparison.OrdinalIgnoreCase ) ) {
baseUrl = baseUrl + "/";
}
return VirtualPathUtility.ToAbsolute( baseUrl + url );
}
private static string ConvertUrlsToAbsolute( string baseUrl, string content )
{
if ( string.IsNullOrWhiteSpace( content ) ) {
return content;
}
return new Regex( "url\(['\"]?(?<url>[^)]+?)['\"]?\)" )
.Replace( content, ( match =>
"url(" + RebaseUrlToAbsolute( baseUrl, match.Groups["url"].Value ) + ")" ) );
}
}
}
}
Use it like StyleBundle
:
像这样使用它StyleBundle
:
bundles.Add( new ProperStyleBundle( "~/styles/ui" )
.Include( "~/Content/Themes/cm_default/style.css" )
.Include( "~/Content/themes/custom-theme/jquery-ui-1.8.23.custom.css" )
.Include( "~/Content/DataTables-1.9.4/media/css/jquery.dataTables.css" )
.Include( "~/Content/DataTables-1.9.4/extras/TableTools/media/css/TableTools.css" ) );
回答by Ben Foster
As of v1.1.0-alpha1 (pre release package) the framework uses the VirtualPathProvider
to access files rather than touching the physical file system.
从 v1.1.0-alpha1(预发布包)开始,该框架使用VirtualPathProvider
来访问文件而不是接触物理文件系统。
The updated transformer can be seen below:
更新后的变压器如下所示:
public class StyleRelativePathTransform
: IBundleTransform
{
public void Process(BundleContext context, BundleResponse response)
{
Regex pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\s*\)", RegexOptions.IgnoreCase);
response.Content = string.Empty;
// open each of the files
foreach (var file in response.Files)
{
using (var reader = new StreamReader(file.Open()))
{
var contents = reader.ReadToEnd();
// apply the RegEx to the file (to change relative paths)
var matches = pattern.Matches(contents);
if (matches.Count > 0)
{
var directoryPath = VirtualPathUtility.GetDirectory(file.VirtualPath);
foreach (Match match in matches)
{
// this is a path that is relative to the CSS file
var imageRelativePath = match.Groups[2].Value;
// get the image virtual path
var imageVirtualPath = VirtualPathUtility.Combine(directoryPath, imageRelativePath);
// convert the image virtual path to absolute
var quote = match.Groups[1].Value;
var replace = String.Format("url({0}{1}{0})", quote, VirtualPathUtility.ToAbsolute(imageVirtualPath));
contents = contents.Replace(match.Groups[0].Value, replace);
}
}
// copy the result into the response.
response.Content = String.Format("{0}\r\n{1}", response.Content, contents);
}
}
}
}
回答by Ciprian Gavrilovici
Here is a Bundle Transform that will replace css urls with urls relative to that css file. Just add it to your bundle and it should fix the issue.
这是一个捆绑转换,它将用相对于该 css 文件的 url 替换 css url。只需将它添加到您的捆绑包中,它就可以解决问题。
public class CssUrlTransform: IBundleTransform
{
public void Process(BundleContext context, BundleResponse response) {
Regex exp = new Regex(@"url\([^\)]+\)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
foreach (FileInfo css in response.Files) {
string cssAppRelativePath = css.FullName.Replace(context.HttpContext.Request.PhysicalApplicationPath, context.HttpContext.Request.ApplicationPath).Replace(Path.DirectorySeparatorChar, '/');
string cssDir = cssAppRelativePath.Substring(0, cssAppRelativePath.LastIndexOf('/'));
response.Content = exp.Replace(response.Content, m => TransformUrl(m, cssDir));
}
}
private string TransformUrl(Match match, string cssDir) {
string url = match.Value.Substring(4, match.Length - 5).Trim('\'', '"');
if (url.StartsWith("http://") || url.StartsWith("data:image")) return match.Value;
if (!url.StartsWith("/"))
url = string.Format("{0}/{1}", cssDir, url);
return string.Format("url({0})", url);
}
}
回答by thepirat000
Grinn solution is great.
格林解决方案很棒。
However it doesn't work for me when there are parent folder relative references in the url.
i.e. url('../../images/car.png')
但是,当 url 中有父文件夹相对引用时,它对我不起作用。IEurl('../../images/car.png')
So, I slightly changed the Include
method in order to resolve the paths for each regex match, allowing relative paths and also to optionally embed the images in the css.
因此,Include
为了解析每个正则表达式匹配的路径,我稍微更改了方法,允许使用相对路径,还可以选择将图像嵌入到 css 中。
I also changed the IF DEBUG to check BundleTable.EnableOptimizations
instead of HttpContext.Current.IsDebuggingEnabled
.
我还将 IF DEBUG 更改为 checkBundleTable.EnableOptimizations
而不是HttpContext.Current.IsDebuggingEnabled
.
public new Bundle Include(params string[] virtualPaths)
{
if (!BundleTable.EnableOptimizations)
{
// Debugging. Bundling will not occur so act normal and no one gets hurt.
base.Include(virtualPaths.ToArray());
return this;
}
var bundlePaths = new List<string>();
var server = HttpContext.Current.Server;
var pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\s*\)", RegexOptions.IgnoreCase);
foreach (var path in virtualPaths)
{
var contents = File.ReadAllText(server.MapPath(path));
var matches = pattern.Matches(contents);
// Ignore the file if no matches
if (matches.Count == 0)
{
bundlePaths.Add(path);
continue;
}
var bundlePath = (System.IO.Path.GetDirectoryName(path) ?? string.Empty).Replace(@"\", "/") + "/";
var bundleUrlPath = VirtualPathUtility.ToAbsolute(bundlePath);
var bundleFilePath = string.Format("{0}{1}.bundle{2}",
bundlePath,
System.IO.Path.GetFileNameWithoutExtension(path),
System.IO.Path.GetExtension(path));
// Transform the url (works with relative path to parent folder "../")
contents = pattern.Replace(contents, m =>
{
var relativeUrl = m.Groups[2].Value;
var urlReplace = GetUrlReplace(bundleUrlPath, relativeUrl, server);
return string.Format("url({0}{1}{0})", m.Groups[1].Value, urlReplace);
});
File.WriteAllText(server.MapPath(bundleFilePath), contents);
bundlePaths.Add(bundleFilePath);
}
base.Include(bundlePaths.ToArray());
return this;
}
private string GetUrlReplace(string bundleUrlPath, string relativeUrl, HttpServerUtility server)
{
// Return the absolute uri
Uri baseUri = new Uri("http://dummy.org");
var absoluteUrl = new Uri(new Uri(baseUri, bundleUrlPath), relativeUrl).AbsolutePath;
var localPath = server.MapPath(absoluteUrl);
if (IsEmbedEnabled && File.Exists(localPath))
{
var fi = new FileInfo(localPath);
if (fi.Length < 0x4000)
{
// Embed the image in uri
string contentType = GetContentType(fi.Extension);
if (null != contentType)
{
var base64 = Convert.ToBase64String(File.ReadAllBytes(localPath));
// Return the serialized image
return string.Format("data:{0};base64,{1}", contentType, base64);
}
}
}
// Return the absolute uri
return absoluteUrl;
}
Hope it helps, regards.
希望有帮助,问候。