MVC4 StyleBundle ne résout pas les images

ma question est similaire à celle-ci:

ASP.NET MVC 4 Minification & Background Images

sauf que je veux m'en tenir au bundling de MVC si je peux. Je suis en train d'avoir un accident de cerveau en essayant de comprendre ce que le modèle correct est pour spécifier des faisceaux de style tels que CSS autonome et des ensembles d'image tels que jQuery UI travail.

j'ai un site MVC typique structure avec /Content/css/ qui contient mon CSS de base tel que styles.css . Dans ce dossier css j'ai aussi des sous-dossiers comme /jquery-ui qui contient son fichier css plus un dossier /images . Les chemins d'Image dans le CSS de jQuery UI sont relatifs à ce dossier et je ne veux pas les embrouiller.

comme je le comprends, quand je spécifie un StyleBundle je dois spécifier un chemin virtuel qui ne correspond pas aussi à un vrai chemin de contenu, parce que (en supposant que j'ignore routes to Content) IIS essaierait alors de résoudre ce chemin en tant que Fichier physique. Donc je spécifie:

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

rendu en utilisant:

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

je peux voir la demande de sortir:

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

ceci renvoie la réponse CSS correcte et réduite. Mais ensuite le navigateur envoie une demande pour une image relativement liée comme:

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

qui est un 404 .

je comprends que la dernière partie de mon URL jquery-ui est une URL sans extension, un gestionnaire pour mon paquet, donc je peux voir pourquoi la demande relative pour l'image est tout simplement /styles/images/ .

donc ma question Est Quelle est la bonne façon de gérer cette situation?

281
demandé sur Community 2012-07-06 08:39:42

15 réponses

selon ce fil de discussion sur MVC4 CSS bundling and image references , si vous définissez votre bundle comme:

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

où vous définissez le faisceau sur le même chemin que les fichiers source qui composent le faisceau, les chemins d'image relatifs fonctionneront toujours. La dernière partie du chemin de faisceau est vraiment le file name pour ce faisceau spécifique (i.e., /bundle peut être n'importe quel nom que vous aimez).

Cela ne fonctionnera que si vous regroupez des CSS à partir du même dossier (ce qui, je pense, est logique du point de vue du regroupement).

mise à Jour

comme dans le commentaire ci-dessous de @Hao Kung, alternativement cela peut maintenant être obtenu en appliquant un CssRewriteUrlTransformation ( Modifier les références D'URL relatives aux fichiers CSS lorsqu'ils sont groupés ).

NOTE: je n'ai pas confirmé les commentaires concernant les réécrire sur des chemins absolus dans un répertoire virtuel, donc cela peut ne pas fonctionner pour tout le monde (?).

bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
                   .Include("~/Content/css/jquery-ui/*.css",
                    new CssRewriteUrlTransform()));
352
répondu Chris Baxter 2016-10-24 13:13:12

Grinn / ThePirat solution works well.

Je n'ai pas aimé qu'il nouveauté la méthode Include sur bundle, et qu'il a créé des fichiers temporaires dans le répertoire de contenu. (ils ont fini par être enregistrés, déployés, et le service n'a pas commencé!)

donc pour suivre la conception de Bundling, j'ai choisi d'effectuer essentiellement le même code, mais dans une mise en œuvre IBundleTransform::

class StyleRelativePathTransform
    : IBundleTransform
{
    public StyleRelativePathTransform()
    {
    }

    public void Process(BundleContext context, BundleResponse response)
    {
        response.Content = String.Empty;

        Regex pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\s*\)", RegexOptions.IgnoreCase);
        // open each of the files
        foreach (FileInfo cssFileInfo in response.Files)
        {
            if (cssFileInfo.Exists)
            {
                // apply the RegEx to the file (to change relative paths)
                string contents = File.ReadAllText(cssFileInfo.FullName);
                MatchCollection matches = pattern.Matches(contents);
                // Ignore the file if no match 
                if (matches.Count > 0)
                {
                    string cssFilePath = cssFileInfo.DirectoryName;
                    string cssVirtualPath = context.HttpContext.RelativeFromAbsolutePath(cssFilePath);
                    foreach (Match match in matches)
                    {
                        // this is a path that is relative to the CSS file
                        string relativeToCSS = match.Groups[2].Value;
                        // combine the relative path to the cssAbsolute
                        string absoluteToUrl = Path.GetFullPath(Path.Combine(cssFilePath, relativeToCSS));

                        // make this server relative
                        string serverRelativeUrl = context.HttpContext.RelativeFromAbsolutePath(absoluteToUrl);

                        string quote = match.Groups[1].Value;
                        string replace = String.Format("url({0}{1}{0})", quote, serverRelativeUrl);
                        contents = contents.Replace(match.Groups[0].Value, replace);
                    }
                }
                // copy the result into the response.
                response.Content = String.Format("{0}\r\n{1}", response.Content, contents);
            }
        }
    }
}

et ensuite enveloppé ceci dans une implémentation globale:

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

Exemple D'Utilisation:

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

voici ma méthode d'extension pour 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
répondu AcidPAT 2012-11-16 19:02:55

mieux encore (IMHO) mettre en œuvre un paquet personnalisé qui fixe les chemins d'image. J'ai écrit pour mon application.

using System;
using System.Collections.Generic;
using IO = System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Optimization;

...

public class StyleImagePathBundle : Bundle
{
    public StyleImagePathBundle(string virtualPath)
        : base(virtualPath, new IBundleTransform[1]
      {
        (IBundleTransform) new CssMinify()
      })
    {
    }

    public StyleImagePathBundle(string virtualPath, string cdnPath)
        : base(virtualPath, cdnPath, new IBundleTransform[1]
      {
        (IBundleTransform) new CssMinify()
      })
    {
    }

    public new Bundle Include(params string[] virtualPaths)
    {
        if (HttpContext.Current.IsDebuggingEnabled)
        {
            // Debugging. Bundling will not occur so act normal and no one gets hurt.
            base.Include(virtualPaths.ToArray());
            return this;
        }

        // In production mode so CSS will be bundled. Correct image paths.
        var bundlePaths = new List<string>();
        var svr = HttpContext.Current.Server;
        foreach (var path in virtualPaths)
        {
            var pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\s*\)", RegexOptions.IgnoreCase);
            var contents = IO.File.ReadAllText(svr.MapPath(path));
            if(!pattern.IsMatch(contents))
            {
                bundlePaths.Add(path);
                continue;
            }


            var bundlePath = (IO.Path.GetDirectoryName(path) ?? string.Empty).Replace(@"\", "/") + "/";
            var bundleUrlPath = VirtualPathUtility.ToAbsolute(bundlePath);
            var bundleFilePath = String.Format("{0}{1}.bundle{2}",
                                               bundlePath,
                                               IO.Path.GetFileNameWithoutExtension(path),
                                               IO.Path.GetExtension(path));
            contents = pattern.Replace(contents, "url(" + bundleUrlPath + ")");
            IO.File.WriteAllText(svr.MapPath(bundleFilePath), contents);
            bundlePaths.Add(bundleFilePath);
        }
        base.Include(bundlePaths.ToArray());
        return this;
    }

}

Pour l'utiliser, faites:

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

...plutôt...

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

ce qu'il fait est (lorsqu'il n'est pas en mode débogage) cherche url(<something>) et le remplace par url(<absolute\path\to\something>) . J'ai écrit ce truc il y a 10 secondes pour qu'il ait besoin d'un peu de retouche. J'ai prise en compte des URLs pleinement qualifiées et des DataURIs base64 en s'assurant qu'il n'y a pas de colons (:) dans le chemin D'URL. Dans notre environnement, les images résident normalement dans le même dossier que leurs fichiers css, mais je l'ai testé avec les deux dossiers parent ( url(../someFile.png) ) et les dossiers enfant ( url(someFolder/someFile.png ).

20
répondu Grinn 2014-06-09 18:03:22

Il n'est pas nécessaire de spécifier une transformation ou un fou sous-répertoire chemins. Après beaucoup de dépannage Je l'ai isolé à cette règle "simple" (est-ce un bug?)...

si votre chemin de faisceau ne commence pas avec la racine relative des articles étant inclus, alors la racine de l'application web ne sera pas prise en compte.

ressemble plus à un bug pour moi, mais de toute façon c'est comme ça que vous le corrigez avec la version actuelle .net 4.51. Peut-être les autres réponses étaient nécessaires sur les ASP.NET construit, ne peut pas dire n'a pas le temps de tester rétrospectivement tout cela.

Pour clarifier, voici un exemple:

j'ai ces fichiers...

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

puis configurer le paquet comme...

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

et le rendre semblable...

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

et obtenir le "comportement" (bug), les fichiers CSS eux-mêmes ont la racine de l'application (par exemple "http://localhost:1234/MySite/Content/Site.css") mais l'image CSS dans toutes les start " / Content/Images/..."ou "/Images/..."selon que j'ajoute le transform ou pas.

a même essayé de créer le dossier "Bundles" pour voir s'il s'agissait du chemin existant ou non, mais cela n'a rien changé. La solution au problème est en fait l'exigence que le nom du paquet doit commencer par la racine du chemin.

ce qui signifie que cet exemple est fixe en enregistrant et en rendant le chemin de faisceau comme..

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

donc, bien sûr, vous pourriez dire que C'est RTFM, mais je suis tout à fait sûr que moi et d'autres ont ramassé ceci "~/Bundles/..."chemin à partir du modèle par défaut ou quelque part dans la documentation à MSDN ou ASP.NET site web, ou simplement trébuché sur elle parce qu'en fait c'est un nom tout à fait logique pour un chemin virtuel et fait du sens de choisir de tels chemins virtuels qui n'entrent pas en conflit avec les répertoires réels.

De toute façon, c'est la façon dont il est. Microsoft voir pas de bogue. Je ne suis pas d'accord avec cela, soit il devrait fonctionner comme prévu ou une exception devrait être lancée, ou une dérogation supplémentaire à l'ajout du chemin de faisceau qui opte pour inclure l'application root ou non. Je ne peux pas imaginer pourquoi quelqu'un ne voudrait pas que la racine de l'application soit incluse alors qu'il y en avait une (normalement sauf si vous avez installé votre site Web avec un alias DNS/racine du site Web par défaut). Donc en fait ça devrait être la valeur par défaut de toute façon.

12
répondu Tony Wall 2014-06-12 16:37:36

j'ai trouvé que CssRewriteUrlTransform ne fonctionne pas si vous faites référence à un fichier *.css et que vous avez le fichier *.min.css associé dans le même dossier.

pour corriger cela, soit supprimer le fichier *.min.css ou le référencer directement dans votre paquet:

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

après cela, vos URLs seront transformées correctement et vos images devraient être correctement résolues.

9
répondu ajbeaven 2014-12-12 00:18:42

bien que la réponse de Chris Baxter aide avec le problème original, il ne fonctionne pas dans mon cas lorsque l'application est hébergée dans le répertoire virtuel . Après avoir examiné les options, j'ai terminé avec DIY solution.

La classe

ProperStyleBundle inclut le code emprunté à l'original CssRewriteUrlTransform pour transformer correctement les chemins relatifs dans le répertoire virtuel. Il jette également si le fichier n'existe pas et empêche la réorganisation des fichiers dans le paquet (code repris de 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 ) + ")" ) );
            }
        }
    }
}

utiliser comme 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
répondu nrodic 2015-01-27 15:41:45

peut-être que je suis partial, mais j'aime assez ma solution car il ne fait pas de transformation, regex etc et il a la moindre quantité de code:)

cela fonctionne pour un site hébergé comme un répertoire virtuel dans un Site Web IIS et comme un site Web racine sur IIS

donc j'ai créé une implantation de IItemTransform encapsulé le CssRewriteUrlTransform et utilisé VirtualPathUtility pour fixer le chemin et appeler le code existant:

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

semble fonctionner pour moi?

7
répondu SimonGates 2015-06-16 14:46:37

à partir de v1.1.0-alpha1 (pré release package) le framework utilise le VirtualPathProvider pour accéder aux fichiers plutôt que de toucher au système de fichiers physique.

le transformateur mis à jour peut être vu ci-dessous:

public class StyleRelativePathTransform
    : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse response)
    {
        Regex pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\s*\)", RegexOptions.IgnoreCase);

        response.Content = string.Empty;

        // open each of the files
        foreach (var file in response.Files)
        {
            using (var reader = new StreamReader(file.Open()))
            {
                var contents = reader.ReadToEnd();

                // apply the RegEx to the file (to change relative paths)
                var matches = pattern.Matches(contents);

                if (matches.Count > 0)
                {
                    var directoryPath = VirtualPathUtility.GetDirectory(file.VirtualPath);

                    foreach (Match match in matches)
                    {
                        // this is a path that is relative to the CSS file
                        var imageRelativePath = match.Groups[2].Value;

                        // get the image virtual path
                        var imageVirtualPath = VirtualPathUtility.Combine(directoryPath, imageRelativePath);

                        // convert the image virtual path to absolute
                        var quote = match.Groups[1].Value;
                        var replace = String.Format("url({0}{1}{0})", quote, VirtualPathUtility.ToAbsolute(imageVirtualPath));
                        contents = contents.Replace(match.Groups[0].Value, replace);
                    }

                }
                // copy the result into the response.
                response.Content = String.Format("{0}\r\n{1}", response.Content, contents);
            }
        }
    }
}
6
répondu Ben Foster 2012-11-30 12:04:55

Voici une transformation de faisceau qui remplacera les urls css avec les urls relatives à ce fichier css. Il suffit de l'ajouter à votre paquet et il devrait résoudre le problème.

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
répondu Ciprian Gavrilovici 2013-03-26 13:00:29

une autre option serait d'utiliser le module de réécriture D'URL de L'IIS pour mapper le dossier image du paquet virtuel vers le dossier image physique. Voici un exemple de règle de réécriture que vous pouvez utiliser pour un paquet appelé "~/bundles/yourpage/styles" - notez les correspondances regex sur les caractères alphanumériques ainsi que les traits d'Union, les underscores et les périodes, qui sont communs dans les noms de fichier image.

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

Cette approche crée un peu de surcharge, mais vous permet d'avoir plus de contrôle sur vos noms de paquets, et réduit également le nombre de paquets que vous pouvez avoir à référence sur une page. Bien sûr, si vous devez faire référence à plusieurs fichiers CSS tiers qui contiennent des références relatives au chemin d'image, vous ne pouvez toujours pas vous déplacer en créant plusieurs paquets.

4
répondu DanO 2012-09-12 20:03:40

la solution de Grinn est excellente.

cependant ça ne marche pas pour moi quand il y a des références relatives au dossier parent dans l'url. c'est-à-dire url('../../images/car.png')

ainsi, j'ai légèrement modifié la méthode Include afin de résoudre les chemins pour chaque correspondance regex, permettant des chemins relatifs et aussi d'intégrer optionnellement les images dans le css.

j'ai aussi changé le débogage IF pour cocher BundleTable.EnableOptimizations au lieu de HttpContext.Current.IsDebuggingEnabled .

    public new Bundle Include(params string[] virtualPaths)
    {
        if (!BundleTable.EnableOptimizations)
        {
            // Debugging. Bundling will not occur so act normal and no one gets hurt. 
            base.Include(virtualPaths.ToArray());
            return this;
        }
        var bundlePaths = new List<string>();
        var server = HttpContext.Current.Server;
        var pattern = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\s*\)", RegexOptions.IgnoreCase);
        foreach (var path in virtualPaths)
        {
            var contents = File.ReadAllText(server.MapPath(path));
            var matches = pattern.Matches(contents);
            // Ignore the file if no matches
            if (matches.Count == 0)
            {
                bundlePaths.Add(path);
                continue;
            }
            var bundlePath = (System.IO.Path.GetDirectoryName(path) ?? string.Empty).Replace(@"\", "/") + "/";
            var bundleUrlPath = VirtualPathUtility.ToAbsolute(bundlePath);
            var bundleFilePath = string.Format("{0}{1}.bundle{2}",
                                               bundlePath,
                                               System.IO.Path.GetFileNameWithoutExtension(path),
                                               System.IO.Path.GetExtension(path));
            // Transform the url (works with relative path to parent folder "../")
            contents = pattern.Replace(contents, m =>
            {
                var relativeUrl = m.Groups[2].Value;
                var urlReplace = GetUrlReplace(bundleUrlPath, relativeUrl, server);
                return string.Format("url({0}{1}{0})", m.Groups[1].Value, urlReplace);
            });
            File.WriteAllText(server.MapPath(bundleFilePath), contents);
            bundlePaths.Add(bundleFilePath);
        }
        base.Include(bundlePaths.ToArray());
        return this;
    }


    private string GetUrlReplace(string bundleUrlPath, string relativeUrl, HttpServerUtility server)
    {
        // Return the absolute uri
        Uri baseUri = new Uri("http://dummy.org");
        var absoluteUrl = new Uri(new Uri(baseUri, bundleUrlPath), relativeUrl).AbsolutePath;
        var localPath = server.MapPath(absoluteUrl);
        if (IsEmbedEnabled && File.Exists(localPath))
        {
            var fi = new FileInfo(localPath);
            if (fi.Length < 0x4000)
            {
                // Embed the image in uri
                string contentType = GetContentType(fi.Extension);
                if (null != contentType)
                {
                    var base64 = Convert.ToBase64String(File.ReadAllBytes(localPath));
                    // Return the serialized image
                    return string.Format("data:{0};base64,{1}", contentType, base64);
                }
            }
        }
        // Return the absolute uri 
        return absoluteUrl;
    }

J'espère que ça vous aidera.

4
répondu thepirat000 2012-10-23 17:19:26

vous pouvez simplement ajouter un autre niveau de profondeur à votre chemin de faisceau virtuel

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

c'est une réponse super low-tech et une sorte de hack mais ça marche et ça ne requerra pas de pré-traitement. Étant donné la longueur et la complexité de certaines de ces réponses, je préfère le faire de cette façon.

2
répondu Brian Rosamilia 2013-12-23 21:26:25

j'ai eu ce problème avec les paquets ayant des chemins incorrects vers les images et CssRewriteUrlTransform ne pas résoudre les chemins parents relatifs .. correctement (il y avait aussi un problème avec les ressources externes comme webfonts). C'est pourquoi j'ai écrit cette transformation personnalisée (semble faire tout ce qui précède correctement):

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: Je ne l'ai pas réalisé, mais j'ai utilisé des méthodes d'extension personnalisées dans le code. Le code source de ceux-ci est:

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

bien entendu, il devrait être possible de remplacer String.StartsWith(char) par String.StartsWith(string) .

2
répondu jahu 2014-11-13 10:29:46

après peu d'enquête j'ai conclu les suivants: Vous avez 2 options:

  1. "151910920 des" transformations. Paquet très utile pour cela: https://bundletransformer.codeplex.com / vous avez besoin de la transformation suivante pour chaque paquet problématique:

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

avantages: de cette solution, vous pouvez nommer votre paquet tout ce que vous voulez = > vous pouvez combiner des fichiers css dans un paquet de différents répertoires. Inconvénients: vous devez transformer chaque paquet problématique

  1. utilisez la même racine relative pour le nom du faisceau comme où le fichier css est situé. Avantages: il n'y a pas besoin de transformation. Inconvénients: vous avez une limitation sur la combinaison des feuilles css de différents répertoires dans un paquet.
1
répondu Kovács Ede 2016-01-07 20:34:51

CssRewriteUrlTransform a résolu mon problème.

Si votre code ne charge toujours pas les images après avoir utilisé CssRewriteUrlTransform , alors changez le nom de votre fichier css de:

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

à:

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

quelque part .(points) ne sont pas reconnus dans l'url.

0
répondu Nalan Madheswaran 2017-07-01 07:33:07