MVC4 StyleBundle не разрешает изображения
Мой вопрос похож на это:
За исключением того, что я хочу придерживаться собственной комплектации 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/
,
Поэтому мой вопрос: как правильно справиться с этой ситуацией?
15 ответов
Согласно этой теме о связывании css MVC4 и ссылках на изображения, если вы определите свой комплект как:
bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
.Include("~/Content/css/jquery-ui/*.css"));
Если вы определяете пакет по тому же пути, что и исходные файлы, которые составляли пакет, относительные пути к изображениям все равно будут работать. Последняя часть пути расслоения действительно file name
для этого конкретного пакета (то есть, /bundle
может быть любым именем, которое вам нравится).
Это будет работать, только если вы объединяете CSS из одной и той же папки (что, я думаю, имеет смысл с точки зрения объединения).
Обновить
Согласно комментарию ниже @Hao Kung, в качестве альтернативы теперь это может быть достигнуто путем применения CssRewriteUrlTransformation
( Измените относительные URL-ссылки на CSS-файлы в комплекте).
ПРИМЕЧАНИЕ. Я не подтвердил комментарии относительно проблем с перезаписью абсолютных путей в виртуальном каталоге, поэтому это может не сработать для всех (?).
bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
.Include("~/Content/css/jquery-ui/*.css",
new CssRewriteUrlTransform()));
Решение Grinn / ThePirat работает хорошо.
Мне не понравилось, что он new'd метод Include в комплекте и что он создает временные файлы в каталоге содержимого. (они закончили тем, что зарегистрировались, развернули, тогда служба не запустилась!)
Поэтому, чтобы следовать схеме 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*([""']?)([^:)]+)\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(@"\", "/");
}
Еще лучше (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"));
Что он делает (когда не в режиме отладки) ищет url(<something>)
и заменяет его url(<absolute\path\to\something>)
, Я написал эту штуку около 10 секунд назад, так что, возможно, потребуется немного изменить ее. Я учел полностью определенные URL-адреса и baseUR DataURI, убедившись, что в пути URL нет двоеточий (:). В нашей среде изображения обычно находятся в той же папке, что и их CSS-файлы, но я проверил их в обеих родительских папках (url(../someFile.png)
) и дочерние папки (url(someFolder/someFile.png
).
Нет необходимости указывать преобразование или иметь сумасшедшие пути подкаталогов. После долгих проблем я изолировал это "простое" правило (это ошибка?)...
Если путь вашего комплекта не начинается с относительного корня включаемых элементов, то корень веб-приложения не будет учитываться.
Для меня это звучит как большая ошибка, но в любом случае именно так вы и исправляете текущую версию.NET 4.51. Возможно, другие ответы были необходимы в более старых сборках ASP.NET, нельзя сказать, что у меня нет времени на ретроспективное тестирование всего этого.
Чтобы уточнить, вот пример:
У меня есть эти файлы...
~/Content/Images/Backgrounds/Some_Background_Tile.gif
~/Content/Site.css - references the background image relatively, i.e. background: url('Images/...')
Затем настройте пакет как...
BundleTable.Add(new StyleBundle("~/Bundles/Styles").Include("~/Content/Site.css"));
И сделать это как...
@Styles.Render("~/Bundles/Styles")
И получите "поведение" (ошибка), у самих CSS-файлов есть корень приложения (например, "http://localhost:1234/MySite/Content/Site.css"), но изображение CSS внутри всего запуска "/Content/Images/..."или"/Images/..."в зависимости от того, добавляю я преобразование или нет.
Даже попытался создать папку "Связки", чтобы увидеть, связано ли это с существующим путем или нет, но это ничего не изменило. Решение проблемы на самом деле заключается в том, что имя пакета должно начинаться с корня пути.
Значение этого примера исправлено путем регистрации и рендеринга пути пакета как...
BundleTable.Add(new StyleBundle("~/Content/StylesBundle").Include("~/Content/Site.css"));
...
@Styles.Render("~/Content/StylesBundle")
Конечно, вы можете сказать, что это RTFM, но я вполне уверен, что я и другие взяли этот путь "~/Bundles/..." из шаблона по умолчанию или где-то в документации на веб-сайте MSDN или ASP.NET, или просто наткнулся на него, потому что на самом деле это вполне логичное имя для виртуального пути и имеет смысл выбирать такие виртуальные пути, которые не конфликтуют с реальными каталогами.
Во всяком случае, так оно и есть. Microsoft не видит ошибки. Я не согласен с этим: либо он должен работать должным образом, либо должно быть выдано какое-то исключение, либо добавлено дополнительное переопределение для добавления пути к пакету, который выбирает включить корень приложения или нет. Я не могу себе представить, почему кто-то не хотел бы, чтобы корень приложения был включен, когда он был (обычно, если вы не установили свой веб-сайт с псевдонимом DNS / корнем веб-сайта по умолчанию). Так что на самом деле это должно быть по умолчанию в любом случае.
Может быть, я предвзят, но мне очень нравится мое решение, так как оно не выполняет никаких преобразований, регулярных выражений и т. Д., И в нем меньше всего кода:)
Это работает для сайта, размещенного в качестве виртуального каталога на веб-сайте IIS и корневого веб-сайта в IIS.
Итак, я создал Implentation 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()));
}
Кажется, работает нормально для меня?
Я обнаружил, что CssRewriteUrlTransform не запускается, если вы ссылаетесь на *.css
файл и у вас есть связанный *.min.css
файл в той же папке.
Чтобы это исправить, либо удалите *.min.css
Файл или ссылка прямо в вашем комплекте:
bundles.Add(new Bundle("~/bundles/bootstrap")
.Include("~/Libs/bootstrap3/css/bootstrap.min.css", new CssRewriteUrlTransform()));
После этого ваши URL-адреса будут преобразованы правильно, и ваши изображения должны быть правильно разрешены.
Хотя ответ Криса Бакстера помогает с исходной проблемой, он не работает в моем случае, когда приложение размещено в виртуальном каталоге. Изучив варианты, я закончил с DIY решением.
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 ) + ")" ) );
}
}
}
}
Используйте это как 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" ) );
Начиная с v1.1.0-alpha1 (предварительная версия пакета) платформа использует VirtualPathProvider
чтобы получить доступ к файлам, а не касаться физической файловой системы.
Обновленный трансформатор можно увидеть ниже:
public class StyleRelativePathTransform
: IBundleTransform
{
public void Process(BundleContext context, BundleResponse response)
{
Regex pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\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);
}
}
}
}
Вот преобразование Bundle, которое заменит URL CSS на URL относительно этого файла CSS. Просто добавьте его в свой пакет, и это должно решить проблему.
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);
}
}
Гринн решение отлично.
Однако это не работает для меня, когда есть относительные ссылки родительской папки в URL. т.е. url('../../images/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;
}
Надеюсь, что это помогает, привет.
Другой вариант - использовать модуль перезаписи URL IIS для сопоставления папки образа виртуального пакета с папкой физического образа. Ниже приведен пример правила перезаписи, которое вы можете использовать для пакета под названием "~/bundles/yourpage/styles" - обратите внимание на совпадения регулярных выражений для буквенно-цифровых символов, а также дефисов, подчеркиваний и точек, которые часто встречаются в именах файлов изображений,
<rewrite>
<rules>
<rule name="Bundle Images">
<match url="^bundles/yourpage/images/([a-zA-Z0-9\-_.]+)" />
<action type="Rewrite" url="Content/css/jquery-ui/images/{R:1}" />
</rule>
</rules>
</rewrite>
Этот подход создает немного дополнительных накладных расходов, но позволяет вам лучше контролировать имена ваших пакетов, а также уменьшает количество пакетов, на которые вы можете ссылаться на одной странице. Конечно, если вам нужно сослаться на несколько сторонних CSS-файлов, которые содержат относительные ссылки на пути к изображениям, вы все равно не сможете обойтись без создания нескольких пакетов.
Вы можете просто добавить еще один уровень глубины к вашему пути виртуального пакета
//Two levels deep bundle path so that paths are maintained after minification
bundles.Add(new StyleBundle("~/Content/css/css").Include("~/Content/bootstrap/bootstrap.css", "~/Content/site.css"));
Это супер-технологичный ответ и своего рода хак, но он работает и не требует предварительной обработки. Учитывая длину и сложность некоторых из этих ответов, я предпочитаю делать это таким образом.
У меня была эта проблема с пакетами, имеющими неправильный путь к изображениям и CssRewriteUrlTransform
не разрешены относительные родительские пути ..
правильно (была также проблема с внешними ресурсами, такими как веб-шрифты). Вот почему я написал это пользовательское преобразование (похоже, все делает правильно):
public class CssRewriteUrlTransform2 : IItemTransform
{
public string Process(string includedVirtualPath, string input)
{
var pathParts = includedVirtualPath.Replace("~/", "/").Split('/');
pathParts = pathParts.Take(pathParts.Count() - 1).ToArray();
return Regex.Replace
(
input,
@"(url\(['""]?)((?:\/??\.\.)*)(.*?)(['""]?\))",
m =>
{
// Somehow assigning this to a variable is faster than directly returning the output
var output =
(
// Check if it's an aboslute url or base64
m.Groups[3].Value.IndexOf(':') == -1 ?
(
m.Groups[1].Value +
(
(
(
m.Groups[2].Value.Length > 0 ||
!m.Groups[3].Value.StartsWith('/')
)
) ?
string.Join("/", pathParts.Take(pathParts.Count() - m.Groups[2].Value.Count(".."))) :
""
) +
(!m.Groups[3].Value.StartsWith('/') ? "/" + m.Groups[3].Value : m.Groups[3].Value) +
m.Groups[4].Value
) :
m.Groups[0].Value
);
return output;
}
);
}
}
Редактировать: я не понял этого, но я использовал несколько пользовательских методов расширения в коде. Исходный код этих:
/// <summary>
/// Based on: http://stackru.com/a/11773674
/// </summary>
public static int Count(this string source, string substring)
{
int count = 0, n = 0;
while ((n = source.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
{
n += substring.Length;
++count;
}
return count;
}
public static bool StartsWith(this string source, char value)
{
if (source.Length == 0)
{
return false;
}
return source[0] == value;
}
Конечно, должна быть возможность заменить String.StartsWith(char)
с String.StartsWith(string)
,
После небольшого расследования я пришел к выводу следующее: у вас есть 2 варианта:
идти с преобразованиями. Очень полезный пакет для этого: https://bundletransformer.codeplex.com/ вам нужно следующее преобразование для каждого проблемного пакета:
BundleResolver.Current = new CustomBundleResolver(); var cssTransformer = new StyleTransformer(); standardCssBundle.Transforms.Add(cssTransformer); bundles.Add(standardCssBundle);
Преимущества: этого решения вы можете назвать свой пакет как хотите => вы можете объединить CSS-файлы в один пакет из разных каталогов. Недостатки: вам нужно преобразовать каждый проблемный пакет
- Используйте тот же относительный корень для имени пакета, например, где находится файл CSS. Преимущества: нет необходимости в трансформации. Недостатки: у вас есть ограничение на объединение листов CSS из разных каталогов в один пакет.
Просто не забудьте исправить несколько включений CSS в пакете, например:
bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
.Include("~/Content/css/path1/somestyle1.css", "~/Content/css/path2/somestyle2.css"));
Вы не можете просто добавить new CssRewriteUrlTransform()
до конца, как вы можете, с одним файлом CSS, поскольку метод его не поддерживает, поэтому вам нужно использоватьInclude
несколько раз:
bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
.Include("~/Content/css/path1/somestyle1.css", new CssRewriteUrlTransform())
.Include("~/Content/css/path2/somestyle2.css", new CssRewriteUrlTransform()));
CssRewriteUrlTransform
исправил мою проблему.
Если ваш код по-прежнему не загружает изображения после использования CssRewriteUrlTransform
, затем измените имя файла CSS:
.Include("~/Content/jquery/jquery-ui-1.10.3.custom.css", new CssRewriteUrlTransform())
Для того, чтобы:
.Include("~/Content/jquery/jquery-ui.css", new CssRewriteUrlTransform())
Так или иначе.(Точки) не распознаются в URL.