web-dev-qa-db-fra.com

MVC4 StyleBundle ne résout pas les images

Ma question est semblable à ceci:

Images d’image de fond ASP.NET MVC 4

Sauf que je veux m'en tenir au groupement de MVC si je le peux. Je suis en train de subir un crash cérébral en essayant de déterminer quel est le modèle correct pour spécifier des ensembles de styles tels que des ensembles de fichiers CSS et d'images autonomes, tels que l'interface utilisateur jQuery.

J'ai une structure de site MVC typique avec /Content/css/ qui contient mon CSS de base tel que styles.css. Dans ce dossier css, j'ai aussi des sous-dossiers tels que /jquery-ui qui contient son fichier CSS plus un dossier /images. Les chemins d’image dans la CSS jQuery UI sont relatifs à ce dossier et je ne veux pas les déranger.

Si je comprends bien, lorsque je spécifie une variable StyleBundle, je dois spécifier un chemin virtuel qui ne correspond pas également à un chemin de contenu réel, car (en supposant que j'ignore les routes vers le contenu) IIS puis essayez de résoudre ce chemin en tant que fichier physique. Donc je précise:

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 adressée à:

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

Cela renvoie la réponse CSS correcte et minifiée. Mais alors 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 de mon ensemble, afin que je puisse voir pourquoi la demande relative de l'image est simplement /styles/images/.

Donc ma question est quelle est la bonne manière de gérer cette situation?

286
Tom Hall

Selon ce fil sur regroupement css MVC4 et références d’image , si vous définissez votre regroupement comme suit:

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

Lorsque vous définissez l’ensemble sur le même chemin que les fichiers source qui le composent, les chemins d’image relatifs continueront de fonctionner. La dernière partie du chemin du paquet est en réalité le file name de ce paquet spécifique (c.-à-d. /bundle peut être le nom de votre choix).

Cela ne fonctionnera que si vous regroupez des CSS dans le même dossier (ce qui est logique du point de vue du regroupement).

Mettre à jour

Selon le commentaire ci-dessous de @Hao Kung, vous pouvez également y parvenir en appliquant un CssRewriteUrlTransformation ( Modifiez les références d'URL relatives aux fichiers CSS lorsqu'elles sont regroupées ).

REMARQUE: je n'ai pas confirmé de commentaires concernant les problèmes de réécriture sur des chemins absolus dans un répertoire virtuel, ce qui risque de ne pas fonctionner pour tout le monde (?).

bundles.Add(new StyleBundle("~/Content/css/jquery-ui/bundle")
                   .Include("~/Content/css/jquery-ui/*.css",
                    new CssRewriteUrlTransform()));
357
Chris Baxter

La solution Grinn/ThePirat fonctionne bien.

Je n'ai pas aimé que la méthode Include sur bundle soit nouvelle et qu'elle crée des fichiers temporaires dans le répertoire de contenu. (ils ont fini par être enregistrés, déployés, puis le service ne commencerait pas!)

Donc, pour suivre la conception de Bundling, j’ai choisi de reproduire essentiellement le même code, mais dans une implémentation 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);
            }
        }
    }
}

Et ensuite, nous avons encapsulé ceci dans une implémentation 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());
    }
}

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(@"\", "/");
    }
34
AcidPAT

Mieux encore (IMHO) implémenter un Bundle personnalisé qui corrige les chemins de l'image. J'ai écrit un 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*([""']?)([^:)]+)\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;
    }

}

Pour l'utiliser, faites:

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

...au lieu de...

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

Ce qui est fait (quand il n’est pas en mode débogage) cherche url(<something>) et le remplace par url(<absolute\path\to\something>). J'ai écrit la chose il y a environ 10 secondes, donc il faudra peut-être un peu de peaufinage. J'ai pris en compte les URL entièrement qualifiées et les URL de données base64 en m'assurant qu'il n'y a pas de deux points (:) dans le chemin de l'URL. Dans notre environnement, les images résident normalement dans le même dossier que leurs fichiers CSS, mais je l’ai testée avec les dossiers parents (url(../someFile.png)) et les dossiers enfants (url(someFolder/someFile.png).

20
Grinn

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

Si votre chemin d'accès au lot ne commence pas par la racine relative des éléments à inclure, la racine de l'application Web ne sera pas prise en compte.

Cela ressemble plus à un bogue pour moi, mais de toute façon c'est comme ça que vous le corrigez avec la version actuelle .NET 4.51. Les autres réponses étaient peut-être nécessaires sur les anciennes versions d'ASP.NET. Je ne peux pas dire que nous n'avons 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/...')

Ensuite, configurez le paquet comme ...

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

Et le rendre comme ...

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

Et obtenir le "comportement" (bogue), 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 tout démarrer "/ Content/Images/... "ou"/Images/... "selon que j'ajoute ou non la transformation.

Même essayé de créer le dossier "Bundles" pour voir si c'était lié au chemin existant ou non, mais cela ne changeait rien. La solution au problème consiste en réalité à exiger que le nom du paquet commence par le chemin racine.

Cela signifie que cet exemple est corrigé en enregistrant et en rendant le chemin du paquet comme ..

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

Alors, bien sûr, vous pourriez dire que c’est RTFM, mais je suis certain que moi-même et d’autres avons choisi ce chemin "~/Bundles/..." du modèle par défaut ou quelque part dans la documentation du site Web MSDN ou ASP.NET, ou juste tombé dessus parce qu’en fait, c’est un nom assez logique pour un chemin virtuel et qu’il est logique de choisir de tels chemins virtuels qui ne sont pas en conflit avec de vrais répertoires.

Quoi qu'il en soit, c'est comme ça. Microsoft ne voit aucun bug. Je ne suis pas d'accord avec cela, que cela fonctionne comme prévu ou qu'une exception soit générée, ou qu'un remplacement supplémentaire soit ajouté pour ajouter le chemin d'accès au paquet qui choisit d'inclure ou non la racine de l'application. Je ne vois pas pourquoi personne ne voudrait que la racine de l’application soit incluse quand il y en a une (normalement, à moins que vous n’ayez installé votre site Web avec un alias DNS/racine de site Web par défaut). Donc, en fait, cela devrait être le cas par défaut.

12
Tony Wall

J'ai constaté que l'exécution de CssRewriteUrlTransform échoue si vous faites référence à un fichier *.css et que vous avez le fichier *.min.css associé dans le même dossier.

Pour résoudre ce problème, supprimez le fichier *.min.css ou faites-le directement référence dans votre bundle:

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

Ensuite, vos URL seront transformées correctement et vos images devront être correctement résolues.

9
ajbeaven

Peut-être que je suis partial, mais j'aime bien ma solution car elle ne transforme pas, les regex, etc., et elle contient le moins de code possible :)

Cela fonctionne pour un site hébergé en tant que répertoire virtuel dans un site Web IIS et en tant que site Web racine sur IIS

J'ai donc créé une Implantation de IItemTransform encapsulé le CssRewriteUrlTransform et utilisé VirtualPathUtility pour corriger 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-t-il bien fonctionner pour moi?

8
SimonGates

Bien que la réponse de Chris Baxter aide à résoudre le problème initial, cela ne fonctionne pas dans mon cas lorsque l'application est hébergée dans un répertoire virtuel . Après avoir étudié les options, j'ai fini avec la solution de bricolage.

La classe ProperStyleBundle inclut du 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 pris 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 ) + ")" ) );
            }
        }
    }
}

Utilisez-le 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
nrodic

À partir de la version 1.1.0-alpha1 (package de version préliminaire), la structure utilise le VirtualPathProvider pour accéder aux fichiers plutôt que de toucher le 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*([""']?)([^:)]+)\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
Ben Foster

Voici une transformation de paquet qui remplacera les URL CSS par des URL relatives à ce fichier CSS. Ajoutez-le simplement à votre forfait et cela 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

La solution Grinn est géniale.

Cependant, cela ne fonctionne pas pour moi lorsqu'il existe des références relatives au dossier parent dans l'URL. c'est-à-dire url('../../images/car.png')

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

J'ai également changé le IF DEBUG pour vérifier 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*([""']?)([^:)]+)\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;
    }

J'espère que ça aide, salutations.

4
thepirat000

Une autre option consisterait à utiliser le module de réécriture d'URL IIS pour mapper le dossier de l'image de l'ensemble virtuel sur le dossier de l'image physique. Vous trouverez ci-dessous un exemple de règle de réécriture que vous pouvez utiliser pour un ensemble appelé "~/bundles/yourpage/styles". Notez que les correspondances des expressions rationnelles sur les caractères alphanumériques, ainsi que les traits d'union, les traits de soulignement et les points, sont courantes 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 charge supplémentaire, mais vous permet de mieux contrôler les noms de vos ensembles et réduit également le nombre d’ensembles que vous devrez peut-être référencer sur une page. Bien sûr, si vous devez faire référence à plusieurs fichiers CSS tiers contenant des références de chemin d'accès d'image relatifs, vous ne pouvez toujours pas contourner la création de plusieurs ensembles.

4
DanO

J'ai eu ce problème avec des paquets ayant un chemin incorrect vers les images et CssRewriteUrlTransform ne résolvant pas correctement les chemins parent relatifs .. (il y avait aussi un problème avec les ressources externes telles que 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 m'en suis pas rendu compte, 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
jahu

Vous pouvez simplement ajouter un autre niveau de profondeur à votre chemin de paquet 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 très peu technique et une sorte de bidouille, mais cela fonctionne et ne nécessite aucun 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
Brian Rosamilia

Après une petite enquête, j'ai conclu ce qui suit: Vous avez 2 options:

  1. aller avec des transformations. Paquet très utile pour ceci: 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 bundle comme vous le souhaitez => vous pouvez combiner des fichiers CSS en un bundle à partir 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 paquet, à l’emplacement du fichier css. Avantages: il n'y a pas besoin de transformation. Inconvénients: Vous ne pouvez pas combiner des feuilles CSS de différents répertoires dans un seul paquet.
1
Kovács Ede

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

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

À:

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

Certains points (points) ne sont pas reconnus dans l’URL.

0
Nalan Madheswaran