MVC4 StyleBundle не разрешает изображения

Мой вопрос похож на это:

ASP.NET MVC 4 Minification & Background Images

За исключением того, что я хочу придерживаться собственной комплектации 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.

Я понимаю, что последняя часть моего URLjquery-ui URL-адрес без расширения, обработчик для моего пакета, поэтому я могу понять, почему относительный запрос изображения просто/styles/images/.

Итак, мой вопросwhat is the correct way справиться с этой ситуацией?

 Tom Hall06 июл. 2012 г., 07:05
Спасибо за ссылку, кассета выглядит хорошо, и я обязательно ее проверю. Но я хочу придерживаться предоставленного подхода, если это возможно, конечно, это должно быть возможно без путаницы с путями к изображениям в сторонних файлах CSS каждый раз, когда выпускается новая версия. сейчас я сохранил свои ScriptBundles (которые хорошо работают), но вернулся к простым ссылкам CSS, пока не получу разрешение. Приветствия.
 balexandre06 июл. 2012 г., 06:48
после того, как был расстроенover and over again с новой частью Bundling and Minification я перешел кCassete Ведьма теперь свободна и работает намного лучше!
 Luke Puplett05 июн. 2013 г., 19:55
Добавление вероятной ошибки по причинам SEO: Контроллер для пути »/bundles/images/blah.jpg' не найден или не реализует IController.

Ответы на вопрос(15)

Другой вариант - использовать модуль перезаписи URL IIS для сопоставления папки образа виртуального пакета с папкой физического образа. Ниже приведен пример правила перезаписи, которое можно использовать для пакета под названием & quot; ~ / bundles / yourpage / styles & quot; - обратите внимание на совпадения регулярных выражений для буквенно-цифровых символов, а также дефисов, подчеркиваний и точек, которые являются общими в именах файлов изображений.

<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-файлов, которые содержат относительные ссылки на пути к изображениям, вы все равно не сможете обойтись без создания нескольких пакетов.

CssRewriteUrlTransform исправил мою проблему.
Если ваш код по-прежнему не загружает изображения после использованияCssRewriteUrlTransform, затем изменить имя файла css с:

.Include("~/Content/jquery/jquery-ui-1.10.3.custom.css", new CssRewriteUrlTransform())

Для того, чтобы:

.Include("~/Content/jquery/jquery-ui.css", new CssRewriteUrlTransform())

Так или иначе. (Точки) не распознаются в URL.

Вы можете просто добавить еще один уровень глубины к своему пути виртуального пакета

    //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"));

Это супер-технологичный ответ и своего рода хак, но он работает и не требует предварительной обработки. Учитывая длину и сложность некоторых из этих ответов, я предпочитаю делать это таким образом.

 02 июн. 2014 г., 11:45
У меня та же проблема, когда приложение является виртуальным приложением в IIS. Этот ответ помогает мне.
 23 апр. 2014 г., 19:59
Это не помогает, если ваше веб-приложение является виртуальным приложением в IIS. Я имею в виду, что это может работать, но вы должны назвать свое виртуальное приложение IIS как в своем коде, что не то, что вы хотите, не так ли?

Хотя ответ Криса Бакстера помогает решить исходную проблему, в моем случае он не работаетwhen application is hosted in virtual directory, Изучив варианты, я закончил с 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" ) );
 27 янв. 2015 г., 16:43
@ miles82 Конечно! Спасибо за указание на это. Я изменил RebaseUrlToAbsolute ().
 27 янв. 2015 г., 15:36
Хорошее решение, но все равно не работает (так же, как CssRewriteUrlTransform), если у вас есть URI данных в вашем CSS (например, «data: image / png; base64, ...»). Вы не должны изменять URL-адреса, начиная с «data:» в RebaseUrlToAbsolute ().

Я обнаружил, что CssRewriteUrlTransform не запускается, если вы ссылаетесь на*.css файл и у вас есть связанный*.min.css файл в той же папке.

Чтобы это исправить, либо удалите*.min.css файл или ссылка прямо в вашем комплекте:

bundles.Add(new Bundle("~/bundles/bootstrap")
    .Include("~/Libs/bootstrap3/css/bootstrap.min.css", new CssRewriteUrlTransform()));

После того, как вы это сделаете, ваши URL будут преобразованы правильно, и ваши изображения должны быть правильно разрешены.

 20 мар. 2018 г., 00:59
Это решило проблему для меня. Это, конечно, похоже на ошибку. Не имеет смысла, что он должен игнорировать CssRewriteUrlTransform, если он находит ранее существующий файл .min.css.
 17 мар. 2016 г., 17:31
Спасибо! После двух дней поиска в Интернете это первое упоминание, которое я когда-либо видел в CssRewriteUrlTransform, работающем с файлами * .css, но не со связанным файлом * .min.css, который извлекается, когда вы не запускаете отладку. среда. Определенно мне кажется, что это ошибка. Придется вручную проверять тип среды, чтобы определить пакет с незавершенной версией для отладки, но по крайней мере у меня есть обходной путь сейчас!

Возможно, я предвзят, но мне очень нравится мое решение, так как оно не выполняет никаких преобразований, регулярных выражений и т. Д., И в нем меньше всего кода :)

Это работает для сайта, размещенного какVirtual Directory in a IIS Web Site and as a root website on IIS

Итак, я создал ImplentationIItemTransform заключил в капсулу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()));
}

Кажется, работает нормально для меня?

 19 апр. 2017 г., 13:16
Это правильный ответ. Класс CssUrlTransformWrapper, предоставленный платформой, решает проблему, за исключением того, что он не работает, только когда приложение не находится в корне веб-сайта. Эта обертка лаконично устраняет этот недостаток.
 08 янв. 2016 г., 10:28
Это идеально подходит для меня. отличное решение. мой голос +1
Решение Вопроса

По этой темеMVC4 css комплектация и ссылки на изображения, если вы определите свой пакет как:

bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
                   .Include("~/Content/css/jquery-ui/*.css"));

Если вы определяете пакет по тому же пути, что и исходные файлы, которые составляли пакет, относительные пути к изображениям все равно будут работать. Последняя часть пути расслоения действительноfile name для этого конкретного комплекта (то есть,/bundle может быть любым именем, которое вам нравится).

Это будет работать, только если вы объединяете CSS из одной и той же папки (что, я думаю, имеет смысл с точки зрения объединения).

Update

Согласно комментарию ниже @Hao Kung, в качестве альтернативы теперь это может быть достигнуто путем примененияCssRewriteUrlTransformation (Измените относительные URL-ссылки на CSS-файлы в комплекте).

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()));
 15 июл. 2014 г., 16:57
new CssRewriteUrlTransform () хорошо, если у вас есть сайт в IIS. но если это приложение или вспомогательное приложение, это не будет работать, и вам придется прибегнуть к определению вашего пакета в том же месте, что и ваш CSS.
 25 февр. 2014 г., 07:44
Исправлено ли это в Microsoft ASP.NET Web Optimization Framework 1.1.3? Я не нашел какой-либо информации о том, что изменилось в этом?
 14 июл. 2012 г., 00:56
Да, к сожалению, пока в комплекте нет поддержки перезаписи встроенных URL-адресов внутри самого css, вам нужен виртуальный каталог пакета css, соответствующий файлам css перед объединением. Вот почему стандартные шаблоны пакетов не имеют URL-адресов, таких как ~ / bundles / themes, и вместо этого выглядят как структура каталогов: ~ / content / theemes / base / css
 Tom Hall09 июл. 2012 г., 00:41
Легенда! Да, это работает отлично. У меня есть CSS на разных уровнях, но у каждого из них есть свои собственные папки с изображениями, например CSS моего основного сайта находится в корневой папке CSS, а затем внутри него находится jquery-ui со своей собственной папкой с изображениями, поэтому я просто указываю 2 пакета, один для базового CSS-кода и один для пользовательского интерфейса jQuery - что может быть неоптимально в сроки запросов, но жизнь коротка. Ура!
 13 мар. 2013 г., 17:58
Теперь это поддерживается с помощью ItemTransforms, .Include (& quot; ~ / Content / css / jquery-ui / *. Css & quot ;, new CssRewriteUrlTransform ())); в 1.1Beta1 следует исправить эту проблему

После небольшого расследования я пришел к выводу следующее: У вас есть 2 варианта:

go with transformations. Very usefull package for this: https://bundletransformer.codeplex.com/ you need following transformation for every problematic bundle:

BundleResolver.Current = new CustomBundleResolver();
var cssTransformer = new StyleTransformer();
standardCssBundle.Transforms.Add(cssTransformer);
bundles.Add(standardCssBundle);

Преимущества: этого решения вы можете назвать свой пакет как хотите = & gt; Вы можете объединить CSS-файлы в один пакет из разных каталогов. Недостатки: вам нужно преобразовать каждый проблемный пакет

Use the same relative root for the name of the bundle like where the css file is located. Advantages: there is no need for transformation. Disadvantages: You have limitation on combining css sheets from different directories into one bundle.

Начиная с 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);
            }
        }
    }
}
 12 янв. 2013 г., 15:48
На самом деле, что это делает, если заменить относительные URL в 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://stackoverflow.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).

 13 нояб. 2014 г., 11:24
@jao мой плохой Я включил свои собственные методы расширения в код, не осознавая этого.
 13 нояб. 2014 г., 10:18
У меня нет перегрузки String.Count (), которая принимает строку (m.Groups[2].Value.Count("..") не работает.) ИValue.StartsWith('/') тоже не работает, потому что StartsWith ожидает строку вместо символа.
 13 нояб. 2014 г., 11:30
@jao добавил в ответ исходный код этих методов расширения.

Еще лучше (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).

 22 нояб. 2012 г., 17:04
Вы можете также поставить инициализацию регулярных выражений вне цикла. Возможно как статическое свойство только для чтения.
 17 окт. 2012 г., 11:28
Это отличное решение. Я немного изменил ваш Regex, чтобы он также работал с файлами LESS, но оригинальная концепция была именно тем, что мне было нужно. Благодарю.

Вот преобразование 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);
    }

}
 17 нояб. 2014 г., 09:45
@Stiger изменить css.FullName.Replace (на css.VirtualFile.VirtualPath.Replace (
 16 янв. 2015 г., 19:15
Я мог бы использовать это неправильно, но будет ли это foreach переписывать все URL-адреса на каждой итерации и оставлять их относительно последнего файла CSS, который он видел?
 20 июл. 2013 г., 10:22
Это работает для меня, спасибо!
 04 апр. 2014 г., 06:48
Как это использовать? Это показывает мне исключение:cannot convert type from BundleFile to FileInfo

Гринн отлично подходит.

Однако это не работает для меня, когда в 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;
    }

Надеюсь, что это помогает, привет.

 24 нояб. 2014 г., 13:16
Это решение работает, но оно создает временные файлы комплекта.

Решение Grinn / ThePirat работает хорошо.

Мне не понравилось, что он новый метод 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(@"\", "/");
    }
 08 нояб. 2012 г., 19:27
Код, который у вас есть, сейчас не работает для меня. Я пытаюсь это исправить, но подумал, что дам вам знать. Метод context.HttpContext.RelativeFromAbsolutePath не существует. Кроме того, если путь URL начинается с символа & quot; / & quot; (делая его абсолютным), ваш путь объединения логики отключен.
 08 дек. 2014 г., 06:54
@ChrisMarisic, кажется, ваш код не работает - response.Files - это массив BundleFiles, эти объекты не имеют надлежащих связей, таких как «Exists», «DirectoryName» и т.п.
 08 дек. 2014 г., 23:53
@ChrisMarisic, возможно, мне нужно импортировать пространство имен, которое предоставляет методы расширения для класса BundleFile?
 08 нояб. 2012 г., 19:18
Мне это тоже кажется чистым. Благодарю. Я голосую за всех вас троих, потому что это выглядело как командная работа. :)
 24 февр. 2013 г., 22:37
@AcidPAT отличная работа. Логика не сработала, если в URL была строка запроса (некоторые сторонние библиотеки добавляют ее, например, FontAwesome для ссылки .woff.) Это легко исправить. Можно настроить регулярное выражение или исправитьrelativeToCSS перед звонкомPath.GetFullPath().

Нет необходимости указывать преобразование или иметь сумасшедшие пути подкаталогов. После долгих поисков и устранения проблем я выделил это на "простой" правило (это ошибка?) ...

Если путь вашего комплекта не начинается с относительного корня включаемых элементов, то корень веб-приложения не будет учитываться.

Для меня это звучит как большая ошибка, но в любом случае это то, как вы это исправляете в текущей версии .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-файлы имеют корень приложения (например, & quot; http: // localhost: 1234 / MySite / Content / Site.css & quot;), но изображение CSS во всех запусках & quot; / Content / Images / ... & Quot; или & quot; / Images /...& quot; в зависимости от того, добавляю ли я преобразование или нет.

Даже пытался создать "Связки" папка, чтобы увидеть, было ли это связано с существующим путем или нет, но это ничего не изменило. Решение проблемы на самом деле заключается в том, что имя пакета должно начинаться с корня пути.

Значение этого примера исправлено путем регистрации и рендеринга пути пакета, как ..

BundleTable.Add(new StyleBundle("~/Content/StylesBundle").Include("~/Content/Site.css"));
...
@Styles.Render("~/Content/StylesBundle")

Поэтому, конечно, вы можете сказать, что это RTFM, но я уверен, что я и другие подхватили это & quot; ~ / Bundles /...& quot; путь от шаблона по умолчанию или где-то в документации на веб-сайте MSDN или ASP.NET, или просто наткнулся на него, потому что на самом деле это вполне логичное имя для виртуального пути и имеет смысл выбирать такие виртуальные пути, которые не конфликтуют с реальными каталогами ,

Во всяком случае, так оно и есть. Microsoft не видит ошибки. Я не согласен с этим: либо он должен работать должным образом, либо должно быть выдано какое-то исключение, либо добавлено дополнительное переопределение для добавления пути к пакету, который решает включить корень приложения или нет. Я не могу себе представить, почему кто-то не хотел бы, чтобы корень приложения был включен, когда он был (обычно, если вы не установили свой веб-сайт с псевдонимом DNS / корнем веб-сайта по умолчанию). Так что на самом деле это должно быть по умолчанию в любом случае.

 21 окт. 2014 г., 23:55
Мне кажется, самое простое «решение». Другие могут иметь побочные эффекты, как с изображением: данные.
 15 мар. 2016 г., 21:23
Благодарю. Вздох. Однажды я хотел бы потратить больше времени на написание кода, чем на просмотр стека.
 03 февр. 2015 г., 00:38
Нет, это не работает
 09 февр. 2016 г., 19:38
Пошел с этим вариантом, и он работал отлично. Нужно было убедиться, что в каждом пакете были только элементы из одной папки (нельзя включать элементы из других папок или подпапок), что немного раздражает, но пока оно работает, я счастлив! Спасибо за пост.
 04 февр. 2015 г., 08:07
@ MohamedEmaish это работает, вы, вероятно, что-то не так. Узнайте, как отслеживать запросы, например, используйте Fiddler Tool, чтобы увидеть, какие URL запрашиваются браузером. Цель состоит не в том, чтобы жестко кодировать весь относительный путь, чтобы ваш веб-сайт мог быть установлен в разных местах (корневых путях) на одном сервере, или ваш продукт может изменить URL-адрес по умолчанию без необходимости переписывать большую часть веб-сайта. (точка наличия и корневая переменная приложения).

Ваш ответ на вопрос