MVC4 StyleBundle nie rozwiązuje obrazów

Moje pytanie jest podobne do tego:

ASP.NET MVC 4 Minifikacja i obrazy tła

Z wyjątkiem tego, że chcę zostać przy własnych pakietach MVC, jeśli Mogę. Mam awarię mózgu, próbując dowiedzieć się, jaki jest prawidłowy wzór do określania pakietów stylów, takich jak samodzielne zestawy css i obrazów, takie jak jQuery UI.

Mam typową strukturę MVC z /Content/css/, która zawiera mój podstawowy CSS, taki jak styles.css. W tym folderze css I mają również podfoldery, takie jak /jquery-ui, który zawiera plik CSS oraz folder /images. Ścieżki obrazów w jQuery UI CSS są względne do tego folderu i nie chcę z nimi zadzierać.

Jak rozumiem, kiedy określam StyleBundle muszę określić ścieżkę wirtualną, która nie pasuje również do rzeczywistej ścieżki zawartości, ponieważ (zakładając, że ignoruję trasy do zawartości) IIS spróbowałby rozwiązać tę ścieżkę jako plik fizyczny. Więc określam:

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

Rendered użycie:

@Styles.Render("~/Content/styles/jquery-ui")

Widzę, że prośba wychodzi na:

http://localhost/MySite/Content/styles/jquery-ui?v=nL_6HPFtzoqrts9nwrtjq0VQFYnhMjY5EopXsK8cxmg1

To zwraca poprawną, zminifikowaną odpowiedź CSS. Jednak wtedy przeglądarka wysyła żądanie relatywnie połączonego obrazu jako:

http://localhost/MySite/Content/styles/images/ui-bg_highlight-soft_100_eeeeee_1x100.png

Czyli 404.

Rozumiem, że ostatnia część mojego adresu URL jquery-ui jest adresem URL bez rozszerzeń, obsługującym mój pakiet, więc widzę, dlaczego względne żądanie obrazu jest po prostu /styles/images/.

Więc moje pytanie brzmi jaka jest prawidłowa droga poradzenia sobie z tą sytuacją?

Author: Community, 2012-07-06

15 answers

Zgodnie z tym wątkiem na MVC4 CSS i odniesienia do obrazów , jeśli zdefiniujesz swój pakiet jako:

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

Jeśli zdefiniujesz pakiet na tej samej ścieżce co pliki źródłowe, które go utworzyły, względne ścieżki obrazów będą nadal działać. Ostatnia część ścieżki pakietu to tak naprawdę file name dla tego konkretnego pakietu(tzn. /bundle może być dowolną nazwą).

To zadziała tylko wtedy, gdy łączysz CSS z tego samego folderu (co myślę, że ma sens z perspektywy wiązania).

Update

Zgodnie z poniższym komentarzem autorstwa @Hao Kung, alternatywnie można to teraz osiągnąć, stosując CssRewriteUrlTransformation (Zmień względne odnośniki URL do plików CSS, gdy są dołączone ).

uwaga: nie potwierdziłem komentarzy dotyczących problemów z przepisywaniem do ścieżek bezwzględnych w katalogu wirtualnym, więc może to nie działać dla wszystkich (?).

bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
                   .Include("~/Content/css/jquery-ui/*.css",
                    new CssRewriteUrlTransform()));
 352
Author: Chris Baxter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-24 13:13:12

Grinn / thepirat rozwiązanie działa dobrze.

Nie podobało mi się, że nowa metoda Include w pakiecie i że tworzy pliki tymczasowe w katalogu content. (kończyły się odprawą, rozlokowaniem, wtedy serwis nie chciał się uruchomić!)

Aby podążać za projektem Bundlingu, zdecydowałem się wykonać zasadniczo ten sam kod, ale w implementacji 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);
            }
        }
    }
}

I zawijamy to w paczkę:

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());
    }
}

Próbka Sposób użycia:

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

Oto moja metoda rozszerzenia dla 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(@"\", "/");
    }
 33
Author: AcidPAT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-16 19:02:55

Jeszcze lepiej (IMHO) zaimplementuj niestandardowy pakiet, który naprawia ścieżki obrazu. Napisałem jeden do mojej aplikacji.

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;
    }

}

Aby go użyć, wykonaj:

bundles.Add(new StyleImagePathBundle("~/bundles/css").Include(
  "~/This/Is/Some/Folder/Path/layout.css"));

...zamiast...

bundles.Add(new StyleBundle("~/bundles/css").Include(
  "~/This/Is/Some/Folder/Path/layout.css"));

To, co robi, to (gdy nie jest w trybie debugowania) szuka url(<something>) i zastępuje go url(<absolute\path\to\something>). Napisałem to jakieś 10 sekund temu, więc może trzeba to trochę poprawić. Wziąłem pod uwagę w pełni kwalifikowane adresy URL i dane base64 upewniając się, że w ścieżce URL nie ma dwukropków (:). W naszym środowisko, obrazy zwykle znajdują się w tym samym folderze co ich pliki css, ale przetestowałem je zarówno z folderami nadrzędnymi (url(../someFile.png)), jak i podrzędnymi (url(someFolder/someFile.png).

 20
Author: Grinn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-09 18:03:22

Nie jest konieczne określanie transformacji lub ścieżki podkatalogu crazy. Po wielu kłopotach wyizolowałem go z tej "prostej" zasady (czy to błąd?)...

Jeśli ścieżka pakietu nie zaczyna się od względnego katalogu głównego elementów, które są dołączane, to katalog główny aplikacji webowej nie będzie brany pod uwagę.

Dla mnie brzmi to bardziej jak błąd, ale tak to naprawiasz z obecną wersją. NET 4.51. Być może Inne odpowiedzi były potrzebne na starszych ASP.NET buduje, nie można powiedzieć, że nie mają czasu, aby to wszystko przetestować.

Dla wyjaśnienia, oto przykład:

Mam te pliki...
~/Content/Images/Backgrounds/Some_Background_Tile.gif
~/Content/Site.css  - references the background image relatively, i.e. background: url('Images/...')

Następnie Ustaw pakiet jak...

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

I renderować go jak...

@Styles.Render("~/Bundles/Styles")

I uzyskać " zachowanie "(bug), same pliki CSS mają root aplikacji (np. " http://localhost:1234/MySite/Content/Site.css"), ale obraz CSS wewnątrz wszystkich start " / Content / Images/..."lub" / Images/..."w zależności od tego czy dodam przekształcić czy nie.

Próbowałem nawet utworzyć folder "Bundles", aby sprawdzić, czy ma to związek z istniejącą ścieżką, ale to niczego nie zmieniło. Rozwiązaniem problemu jest wymóg, aby Nazwa pakietu zaczynała się od katalogu głównego ścieżki.

Co oznacza, że ten przykład jest naprawiany przez rejestrację i renderowanie ścieżki pakietu..

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

Więc oczywiście można powiedzieć, że to RTFM, ale jestem całkiem pewien, że ja i inni odebraliśmy to " ~ / Bundles/..."ścieżka z domyślnym szablonem lub gdzieś w dokumentacji na MSDN lub ASP.NET strony WWW, lub po prostu natknąłem się na nią, ponieważ w rzeczywistości jest to dość logiczna nazwa dla ścieżki wirtualnej i ma sens wybierać takie ścieżki wirtualne, które nie kolidują z prawdziwymi katalogami.

W każdym razie, tak to już jest. Microsoft nie widzi błędu. Nie zgadzam się z tym, albo powinno działać zgodnie z oczekiwaniami, albo jakiś wyjątek powinien zostać wyrzucony, albo dodatkowe nadpisanie do dodania ścieżki pakietu, która decyduje się na dołączenie root aplikacji czy nie. Nie mogę sobie wyobrazić, dlaczego ktoś nie chciałby, aby root aplikacji włączone, gdy nie było (normalnie, chyba że zainstalowałeś swoją witrynę internetową z aliasem DNS / domyślnym root witryny sieci web). Tak właściwie to i tak powinno być domyślne.
 12
Author: Tony Wall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-12 16:37:36

Odkryłem, że CssRewriteUrlTransform nie działa, jeśli odwołujesz się do pliku *.css i masz powiązany plik *.min.css w tym samym folderze.

Aby to naprawić, usuń plik *.min.css lub odwołaj się do niego bezpośrednio w pakiecie:

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

Po tym, co zrobisz, Twoje adresy URL zostaną poprawnie przekształcone, a obrazy powinny być poprawnie rozwiązane.

 9
Author: ajbeaven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-12 00:18:42

Chociaż odpowiedź Chrisa Baxtera pomaga w oryginalnym problemie, to nie działa w moim przypadku , gdy aplikacja jest hostowana w wirtualnym katalogu . Po zbadaniu opcji skończyłem z rozwiązaniem DIY.

ProperStyleBundle klasa zawiera kod zapożyczony z oryginału CssRewriteUrlTransform aby poprawnie przekształcić ścieżki względne w katalogu wirtualnym. Rzuca również, jeśli plik nie istnieje i zapobiega ponownemu uporządkowaniu plików w pakiecie (kod pobrany z 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 ) + ")" ) );
            }
        }
    }
}

Użyj go jak 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" ) );
 7
Author: nrodic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-27 15:41:45

Może jestem stronniczy, ale bardzo podoba mi się moje rozwiązanie, ponieważ nie robi żadnych przekształceń, regex itp i ma najmniejszą ilość kodu:)

W przeciwieństwie do IIS, w IIS nie ma dostępu do Internetu i nie ma dostępu do Internetu, ale nie ma dostępu do Internetu i nie ma dostępu do Internetu.]}

Więc stworzyłem implantację IItemTransform zamknąłem CssRewriteUrlTransform i użyłem VirtualPathUtility aby naprawić ścieżkę i wywołać istniejący kod:

/// <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()));
}
Wygląda na to, że mi pasuje?
 7
Author: SimonGates,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-16 14:46:37

Od wersji 1.1.0-alpha1 (pre release package) framework używa VirtualPathProvider, Aby uzyskać dostęp do plików, zamiast dotykać fizycznego systemu plików.

Zaktualizowany transformator można zobaczyć poniżej:

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);
            }
        }
    }
}
 6
Author: Ben Foster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-30 12:04:55

Oto transformacja pakietu, która zastąpi adresy URL css adresami URL odnoszącymi się do tego pliku css. Po prostu dodaj go do swojego pakietu i powinien rozwiązać problem.

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);
    }

}
 6
Author: Ciprian Gavrilovici,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-26 13:00:29

Inną opcją byłoby użycie modułu IIS URL Rewrite do mapowania folderu virtual bundle image do folderu fizycznego image. Poniżej znajduje się Przykład reguły przepisywania, której możesz użyć dla pakietu o nazwie "~/bundles/yourpage / styles" - zwróć uwagę, że wyrażenia regularne pasują do znaków alfanumerycznych, a także myślników, podkreślników i kropek, które są powszechne w nazwach plików obrazów.

<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>

Takie podejście tworzy trochę dodatkowych kosztów, ale pozwala na większą kontrolę nad pakietem nazwy, a także zmniejsza liczbę pakietów, które możesz mieć do odwołania na jednej stronie. Oczywiście, jeśli musisz odwoływać się do wielu plików css innych firm, które zawierają względne odniesienia do ścieżek obrazów, nadal nie możesz obejść się z tworzeniem wielu pakietów.

 4
Author: DanO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-12 20:03:40

Rozwiązanie Grinn jest świetne.

Jednak nie działa dla mnie, gdy w adresie url znajdują się odnośniki względne do folderu nadrzędnego. tj. url('../../images/car.png')

Zmieniłem więc nieco metodę Include, aby rozwiązać ścieżki dla każdego dopasowania regex, zezwalając na ścieżki względne, a także opcjonalnie osadzając obrazy w css.

Zmieniłem również DEBUG IF na check BundleTable.EnableOptimizations zamiast 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;
    }
Mam nadzieję, że to pomoże, pozdrawiam.
 4
Author: thepirat000,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-23 17:19:26

Możesz po prostu dodać kolejny poziom głębi do ścieżki Wirtualnego pakietu

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

Jest to super low-tech odpowiedź i rodzaj hack, ale to działa i nie wymaga żadnego wstępnego przetwarzania. Biorąc pod uwagę długość i złożoność niektórych z tych odpowiedzi, wolę robić to w ten sposób.

 2
Author: Brian Rosamilia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-23 21:26:25

Miałem problem z pakietami, które miały niepoprawne ścieżki do obrazów i CssRewriteUrlTransform nie rozwiązywały poprawnie względnych ścieżek nadrzędnych .. (był też problem z zewnętrznymi zasobami, takimi jak fonty). Dlatego napisałem tę niestandardową transformację (wygląda na to, że wszystkie powyższe rzeczy robią poprawnie):

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;
            }
        );
    }
}

Edit: nie zdawałem sobie z tego sprawy, ale użyłem w kodzie niestandardowych metod rozszerzeń. Ich kod źródłowy to:

/// <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;
}

Oczywiście powinno być możliwe zastąpienie String.StartsWith(char) przez String.StartsWith(string).

 2
Author: jahu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-13 10:29:46

Po małym śledztwie doszedłem do następujących wniosków: Masz 2 opcje:

  1. Idź z transformacjami. Bardzo przydatny pakiet do tego: https://bundletransformer.codeplex.com/ dla każdego problematycznego pakietu potrzebna jest następująca transformacja:

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

Zalety: tego rozwiązania możesz nazwać swój pakiet jak chcesz = > możesz łączyć pliki css w jeden pakiet z różnych katalogów. Wady: trzeba przekształcić każdy problematyczny pakiet

  1. Użyj tego samego względnego katalogu głównego dla nazwy pakietu, np. gdzie znajduje się plik css. Zalety: nie ma potrzeby transformacji. Wady: masz ograniczenia dotyczące łączenia arkuszy css z różnych katalogów w jeden pakiet.
 1
Author: Kovács Ede,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-07 20:34:51

CssRewriteUrlTransform naprawiłem mój problem.
Jeśli twój kod nadal nie ładuje obrazów po użyciu CssRewriteUrlTransform, to Zmień nazwę pliku css z:

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

Do:

.Include("~/Content/jquery/jquery-ui.css", new CssRewriteUrlTransform())
Gdzieś .(kropki) nie są rozpoznawane w url.
 0
Author: Nalan Madheswaran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-01 07:33:07