我的问题与此类似:
ASP。NET MVC 4缩小和背景图像
除了我想坚持MVC自己的捆绑如果我可以的话。我有一个大脑崩溃试图找出什么是正确的模式是指定样式包,如独立的css和图像集,如jQuery UI工作。
我有一个典型的MVC网站结构与/Content/css包含我的基本css,如样式。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/。
所以我的问题是,怎样处理这种情况才是正确的?
更好的是(IMHO)实现一个自定义Bundle来修复图像路径。我为我的应用程序写了一个。
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*([""']?)([^:)]+)\1\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($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"));
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).
也许我有偏见,但我很喜欢我的解决方案,因为它不做任何转换,正则表达式等,它有最少的代码:)
这适用于作为IIS网站中的虚拟目录托管的网站和作为IIS上的根网站托管的网站
所以我创建了一个IItemTransform的实现,封装了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()));
}
似乎对我很有效?
咧嘴笑的方法很好。
但是,当url中有父文件夹相对引用时,它对我不起作用。
例如,url(. . / . . /图片/ car.png)
因此,我稍微改变了Include方法,以便为每个正则匹配解析路径,允许相对路径,也可以选择将图像嵌入到css中。
我还更改了IF DEBUG检查BundleTable。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*([""']?)([^:)]+)\1\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;
}
希望能有所帮助,敬礼。
更好的是(IMHO)实现一个自定义Bundle来修复图像路径。我为我的应用程序写了一个。
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*([""']?)([^:)]+)\1\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($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"));
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).
Grinn / ThePirat解决方案效果很好。
我不喜欢它的新包括方法在捆绑,并在内容目录中创建临时文件。(它们最终被检入、部署,然后服务无法启动!)
因此,为了遵循捆绑的设计,我选择执行本质上相同的代码,但在IBundleTransform实现中::
class StyleRelativePathTransform
: IBundleTransform
{
public StyleRelativePathTransform()
{
}
public void Process(BundleContext context, BundleResponse response)
{
response.Content = String.Empty;
Regex pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\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);
}
}
}
}
然后将其封装在一个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());
}
}
示例用法:
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(@"\", "/");
}