web-dev-qa-db-ja.com

MVC4 StyleBundleが画像を解決しない

私の質問はこれに似ています:

ASP.NET MVC 4の縮小と背景画像

できればMVC自身のバンドリングを使い続けたいということを除けば。スタンドアローンのcssやjQuery UIなどの画像セットが機能するようにスタイルバンドルを指定するための正しいパターンを理解しようとすると、クラッシュしてしまいます。

私は/Content/css/のような私のベースCSSを含むstyles.cssを持つ典型的なMVCサイト構造を持っています。そのcssフォルダの中に、私はそのCSSファイルと/jquery-uiフォルダを含む/imagesのようなサブフォルダもあります。 jQuery UI CSSの画像パスはそのフォルダに対する相対パスなので、混乱させたくありません。

私が理解しているように、StyleBundleを指定するとき、実際のコンテンツパスにも一致しない仮想パスを指定する必要があります。(Contentへの経路を無視していると仮定して)IISは解決しようとするからですそのパスを物理ファイルとして。だから私は指定しています:

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

を使用してレンダリング:

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

リクエストが次のように送信されているのがわかります。

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

これは正しい、縮小されたCSS応答を返しています。しかし、ブラウザは比較的リンクされた画像に対するリクエストを次のように送信します。

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

これは404です。

私のURLの最後の部分jquery-uiは私のバンドルのための拡張子のないURLであることを理解しています、それで私はなぜ画像に対する相対的なリクエストが単に/styles/images/であるかを見ることができます。

それで私の質問はこの状況を処理する正しい方法は何ですか?

285
Tom Hall

あなたのバンドルを次のように定義した場合、 MVC4 CSSバンドリングと画像参照 のこのスレッドによると:

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

バンドルを構成しているソースファイルと同じパスにバンドルを定義した場合でも、相対イメージパスは機能します。バンドルパスの最後の部分は、実際にはその特定のバンドルのfile nameです(つまり、/bundleは好きな名前にすることができます)。

これは同じフォルダからCSSを一緒にバンドルしている場合にのみ機能します(バンドルの観点から見て理にかなっていると思います)。

更新

@Hao Kungによる以下のコメントのとおり、代わりにこれはCssRewriteUrlTransformationバンドルされたときの相対URL参照をCSSファイルに変更 )を適用することで達成できるかもしれません。

注:仮想ディレクトリ内の絶対パスへの書き換えに関する問題についてのコメントは確認されていません。したがって、これはすべての人にとってうまくいくとは限りません(?)。

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

Grinn/ThePiratソリューションはうまくいきます。

バンドルのIncludeメソッドが新しくなったこと、およびコンテンツディレクトリに一時ファイルが作成されたことを知りたくありませんでした。 (彼らは最終的にチェックインされ、デプロイされ、そしてサービスは開始されないでしょう!)

そのため、Bundlingの設計に従うために、私は基本的に同じコードを実行することにしましたが、IBundleTransform実装では::

class StyleRelativePathTransform
    : IBundleTransform
{
    public StyleRelativePathTransform()
    {
    }

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

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

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

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

そして、これをBundle Implementationにまとめました。

public class StyleImagePathBundle 
    : Bundle
{
    public StyleImagePathBundle(string virtualPath)
        : base(virtualPath)
    {
        base.Transforms.Add(new StyleRelativePathTransform());
        base.Transforms.Add(new CssMinify());
    }

    public StyleImagePathBundle(string virtualPath, string cdnPath)
        : base(virtualPath, cdnPath)
    {
        base.Transforms.Add(new StyleRelativePathTransform());
        base.Transforms.Add(new CssMinify());
    }
}

使用例

static void RegisterBundles(BundleCollection bundles)
{
...
    bundles.Add(new StyleImagePathBundle("~/bundles/Bootstrap")
            .Include(
                "~/Content/css/bootstrap.css",
                "~/Content/css/bootstrap-responsive.css",
                "~/Content/css/jquery.fancybox.css",
                "~/Content/css/style.css",
                "~/Content/css/error.css",
                "~/Content/validation.css"
            ));

これがRelativeFromAbsolutePathの拡張メソッドです。

   public static string RelativeFromAbsolutePath(this HttpContextBase context, string path)
    {
        var request = context.Request;
        var applicationPath = request.PhysicalApplicationPath;
        var virtualDir = request.ApplicationPath;
        virtualDir = virtualDir == "/" ? virtualDir : (virtualDir + "/");
        return path.Replace(applicationPath, virtualDir).Replace(@"\", "/");
    }
33
AcidPAT

いっそのこと(私見)は、画像パスを修正するカスタムバンドルを実装します。私は自分のアプリ用にこれを書きました。

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

...

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

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

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

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


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

}

それを使うには:

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

...の代わりに...

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

それがすることは(デバッグモードでないとき)、url(<something>)を探し、それをurl(<absolute\path\to\something>)で置き換えることです。私は10秒ほど前に書いたので少し調整が必要かもしれません。 URLパスにコロン(:)がないことを確認することで、完全修飾URLとbase64 DataURIを考慮しました。私たちの環境では、画像は通常それらのcssファイルと同じフォルダにありますが、私は親フォルダ(url(../someFile.png))と子フォルダ(url(someFolder/someFile.png)の両方でそれをテストしました。

20
Grinn

変換を指定したり、変わったサブディレクトリパスを指定したりする必要はありません。多くのトラブルシューティングの後、私はそれをこの "単純な"ルールに分離しました(それはバグですか?)...

バンドルパスが含まれるアイテムの相対ルートで始まっていない場合、Webアプリケーションのルートは考慮されません。

私にとってはもっとバグのように思えるかもしれませんが、とにかく現在の.NET 4.51バージョンでそれを修正する方法です。おそらく他の答えは古いASP.NETビルドに必要でした、遡及的にそれらすべてをテストする時間がないと言うことはできません。

明確にするために、これは例です:

これらのファイルがあります。

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

それからバンドルを次のように設定します。

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

そしてそれを次のようにレンダリングします。

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

そして "動作"(バグ)を取得してください。CSSファイル自体はアプリケーションルート(例: "http:// localhost:1234/MySite/Content/Site.css")を持ちますが、CSSイメージはすべての中から始まります "/ Content/Images/... "または"/Images/... "は、変換を追加するかどうかによって異なります。

「Bundles」フォルダを作成して、パスが存在するかどうかを確認することもできましたが、それで何も変更されませんでした。この問題の解決策は、実際にはバンドルの名前がパスのルートで始まっていなければならないという要件です。

この例は、バンドルパスを登録およびレンダリングすることで修正されることを意味しています。

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

もちろん、これはRTFMと言えるでしょうが、私や他の人がデフォルトのテンプレートからこの "〜/ Bundles/..."パスを選択したか、MSDNまたはASP.NET Webサイトのドキュメントのどこかにあることを確信しています。実際にそれは仮想パスのための非常に論理的な名前であり、実際のディレクトリと衝突しないような仮想パスを選択することは理にかなっているのでそれにつまずいただけです。

とにかく、それがそのままです。マイクロソフトにはバグはありません。私はこれに賛成しません、それは予想通りに動くべきであるか例外が投げられるべきであるか、あるいはアプリケーションルートを含むかどうかを選択するバンドルパスを追加することへの追加の上書きです。アプリケーションルートがあったのに、誰かがそのアプリケーションルートを含めたくないと思う理由は想像できません(通常、あなたのウェブサイトにDNSエイリアス/デフォルトのウェブサイトルートをインストールしていない限り)。それで、実際にはそれはとにかくデフォルトであるべきです。

12
Tony Wall

*.cssファイルを参照していて、関連する*.min.cssファイルが同じフォルダーにある場合、CssRewriteUrlTransformは実行に失敗することがわかりました。

これを修正するには、*.min.cssファイルを削除するか、バンドル内で直接参照します。

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

その後、あなたのURLは正しく変換され、あなたの画像は正しく解決されるでしょう。

9
ajbeaven

たぶん、私は偏っています、しかし、私はそれが少しの変換もしない、正規表現などをしない、そしてそれが最小のコードを持っているので私の解決策がとても好きです:)

これはIIS Webサイトの仮想ディレクトリとして、またIISのルートWebサイトとしてホストされているサイトで機能します

そのため、IItemTransformをカプセル化したCssRewriteUrlTransformのImplementationを作成し、VirtualPathUtilityを使用してパスを修正し、既存のコードを呼び出しました。

/// <summary>
/// Is a wrapper class over CssRewriteUrlTransform to fix url's in css files for sites on IIS within Virutal Directories
/// and sites at the Root level
/// </summary>
public class CssUrlTransformWrapper : IItemTransform
{
    private readonly CssRewriteUrlTransform _cssRewriteUrlTransform;

    public CssUrlTransformWrapper()
    {
        _cssRewriteUrlTransform = new CssRewriteUrlTransform();
    }

    public string Process(string includedVirtualPath, string input)
    {
        return _cssRewriteUrlTransform.Process("~" + VirtualPathUtility.ToAbsolute(includedVirtualPath), input);
    }
}


//App_Start.cs
public static void Start()
{
      BundleTable.Bundles.Add(new StyleBundle("~/bundles/fontawesome")
                         .Include("~/content/font-awesome.css", new CssUrlTransformWrapper()));
}

私にとってはうまくいくようですか?

7
SimonGates

Chris Baxterの答えは元々の問題を解決するのに役立ちますが、私の場合はうまくいきませんアプリケーションが仮想ディレクトリでホストされている場合。選択肢を調査した後、私はDIYの解決策を終えました。

ProperStyleBundleクラスには、仮想ディレクトリ内の相対パスを正しく変換するために、元の CssRewriteUrlTransform から借用したコードが含まれています。 fileが存在しない場合にもスローされ、バンドル内のファイルの並べ替えを妨げます( BetterStyleBundle から取得したコード)。

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

namespace MyNamespace
{
    public class ProperStyleBundle : StyleBundle
    {
        public override IBundleOrderer Orderer
        {
            get { return new NonOrderingBundleOrderer(); }
            set { throw new Exception( "Unable to override Non-Ordered bundler" ); }
        }

        public ProperStyleBundle( string virtualPath ) : base( virtualPath ) {}

        public ProperStyleBundle( string virtualPath, string cdnPath ) : base( virtualPath, cdnPath ) {}

        public override Bundle Include( params string[] virtualPaths )
        {
            foreach ( var virtualPath in virtualPaths ) {
                this.Include( virtualPath );
            }
            return this;
        }

        public override Bundle Include( string virtualPath, params IItemTransform[] transforms )
        {
            var realPath = System.Web.Hosting.HostingEnvironment.MapPath( virtualPath );
            if( !File.Exists( realPath ) )
            {
                throw new FileNotFoundException( "Virtual path not found: " + virtualPath );
            }
            var trans = new List<IItemTransform>( transforms ).Union( new[] { new ProperCssRewriteUrlTransform( virtualPath ) } ).ToArray();
            return base.Include( virtualPath, trans );
        }

        // This provides files in the same order as they have been added. 
        private class NonOrderingBundleOrderer : IBundleOrderer
        {
            public IEnumerable<BundleFile> OrderFiles( BundleContext context, IEnumerable<BundleFile> files )
            {
                return files;
            }
        }

        private class ProperCssRewriteUrlTransform : IItemTransform
        {
            private readonly string _basePath;

            public ProperCssRewriteUrlTransform( string basePath )
            {
                _basePath = basePath.EndsWith( "/" ) ? basePath : VirtualPathUtility.GetDirectory( basePath );
            }

            public string Process( string includedVirtualPath, string input )
            {
                if ( includedVirtualPath == null ) {
                    throw new ArgumentNullException( "includedVirtualPath" );
                }
                return ConvertUrlsToAbsolute( _basePath, input );
            }

            private static string RebaseUrlToAbsolute( string baseUrl, string url )
            {
                if ( string.IsNullOrWhiteSpace( url )
                     || string.IsNullOrWhiteSpace( baseUrl )
                     || url.StartsWith( "/", StringComparison.OrdinalIgnoreCase )
                     || url.StartsWith( "data:", StringComparison.OrdinalIgnoreCase )
                    ) {
                    return url;
                }
                if ( !baseUrl.EndsWith( "/", StringComparison.OrdinalIgnoreCase ) ) {
                    baseUrl = baseUrl + "/";
                }
                return VirtualPathUtility.ToAbsolute( baseUrl + url );
            }

            private static string ConvertUrlsToAbsolute( string baseUrl, string content )
            {
                if ( string.IsNullOrWhiteSpace( content ) ) {
                    return content;
                }
                return new Regex( "url\\(['\"]?(?<url>[^)]+?)['\"]?\\)" )
                    .Replace( content, ( match =>
                                         "url(" + RebaseUrlToAbsolute( baseUrl, match.Groups["url"].Value ) + ")" ) );
            }
        }
    }
}

StyleBundleのように使います。

bundles.Add( new ProperStyleBundle( "~/styles/ui" )
    .Include( "~/Content/Themes/cm_default/style.css" )
    .Include( "~/Content/themes/custom-theme/jquery-ui-1.8.23.custom.css" )
    .Include( "~/Content/DataTables-1.9.4/media/css/jquery.dataTables.css" )
    .Include( "~/Content/DataTables-1.9.4/extras/TableTools/media/css/TableTools.css" ) );
7
nrodic

V1.1.0-alpha1(プレリリースパッケージ)以降、フレームワークは物理ファイルシステムに触れるのではなく、VirtualPathProviderを使用してファイルにアクセスします。

更新されたトランスは以下のようになります。

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

        response.Content = string.Empty;

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

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

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

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

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

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

                }
                // copy the result into the response.
                response.Content = String.Format("{0}\r\n{1}", response.Content, contents);
            }
        }
    }
}
6
Ben Foster

これは、CSSのURLをそのCSSファイルを基準にしたURLに置き換えるバンドル変換です。バンドルに追加するだけで問題は解決します。

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

Grinnの解決策は素晴らしいです。

しかし、urlに親フォルダの相対参照があると、うまくいきません。すなわちurl('../../images/car.png')

そこで、正規表現の一致ごとにパスを解決するためにIncludeメソッドを少し変更し、相対パスを許可して、必要に応じてイメージをCSSに埋め込むこともできます。

また、IF DEBUGをBundleTable.EnableOptimizationsではなくHttpContext.Current.IsDebuggingEnabledにチェックするように変更しました。

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


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

それが役立つことを願っています。

4
thepirat000

別のオプションは、IIS URL Rewriteモジュールを使用して仮想バンドルイメージフォルダを物理イメージフォルダにマップすることです。以下は「〜/ bundles/yourpage/styles」というバンドルに使用できる書き換え規則の例です。正規表現は英数字、ハイフン、アンダースコア、ピリオドと一致しています。これらはイメージファイル名で一般的です。 。

<rewrite>
  <rules>
    <rule name="Bundle Images">
      <match url="^bundles/yourpage/images/([a-zA-Z0-9\-_.]+)" />
      <action type="Rewrite" url="Content/css/jquery-ui/images/{R:1}" />
    </rule>
  </rules>
</rewrite>

この方法では多少余分なオーバーヘッドが発生しますが、バンドル名をより細かく制御できるようになり、1ページで参照する必要があるバンドルの数も減ります。もちろん、相対イメージパス参照を含む複数のサードパーティ製cssファイルを参照する必要がある場合でも、複数のバンドルを作成することはできません。

4
DanO

私はこの問題を抱えていました。画像へのパスが正しくなく、CssRewriteUrlTransformが相対的な親パス..を正しく解決していませんでした(ウェブフォントのような外部リソースにも問題がありました)。私がこのカスタムトランスフォームを書いたのはそのためです(上記のすべてを正しく行うように見えます):

public class CssRewriteUrlTransform2 : IItemTransform
{
    public string Process(string includedVirtualPath, string input)
    {
        var pathParts = includedVirtualPath.Replace("~/", "/").Split('/');
        pathParts = pathParts.Take(pathParts.Count() - 1).ToArray();
        return Regex.Replace
        (
            input,
            @"(url\(['""]?)((?:\/??\.\.)*)(.*?)(['""]?\))",
            m => 
            {
                // Somehow assigning this to a variable is faster than directly returning the output
                var output =
                (
                    // Check if it's an aboslute url or base64
                    m.Groups[3].Value.IndexOf(':') == -1 ?
                    (
                        m.Groups[1].Value +
                        (
                            (
                                (
                                    m.Groups[2].Value.Length > 0 ||
                                    !m.Groups[3].Value.StartsWith('/')
                                )
                            ) ?
                            string.Join("/", pathParts.Take(pathParts.Count() - m.Groups[2].Value.Count(".."))) :
                            ""
                        ) +
                        (!m.Groups[3].Value.StartsWith('/') ? "/" + m.Groups[3].Value : m.Groups[3].Value) +
                        m.Groups[4].Value
                    ) :
                    m.Groups[0].Value
                );
                return output;
            }
        );
    }
}

編集:私はそれを認識していませんでしたが、私はいくつかのカスタム拡張メソッドをコードで使用しました。それらのソースコードは次のとおりです。

/// <summary>
/// Based on: http://stackoverflow.com/a/11773674
/// </summary>
public static int Count(this string source, string substring)
{
    int count = 0, n = 0;

    while ((n = source.IndexOf(substring, n, StringComparison.InvariantCulture)) != -1)
    {
        n += substring.Length;
        ++count;
    }
    return count;
}

public static bool StartsWith(this string source, char value)
{
    if (source.Length == 0)
    {
        return false;
    }
    return source[0] == value;
}

もちろん、String.StartsWith(char)String.StartsWith(string)に置き換えることは可能です。

2
jahu

あなたは単にあなたの仮想バンドルパスにもう一つの深さのレベルを加えることができます

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

これは超ローテクな回答であり、一種のハックですが、うまくいきますし、前処理を必要としません。これらの答えのいくつかの長さと複雑さを考えると、私はこのようにするのを好む。

2
Brian Rosamilia

ちょっとした調査の結果、私は次のように結論付けました。

  1. 変換を行います。このための非常に便利なパッケージ: https://bundletransformer.codeplex.com/ あなたはすべての問題のあるバンドルのために次の変換が必要です:

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

利点:このソリューションでは、バンドルに好きな名前を付けることができます。=> cssファイルを異なるディレクトリから1つのバンドルにまとめることができます。デメリット:問題のあるバンドルをすべて変換する必要があります。

  1. Cssファイルがある場所と同じように、バンドルの名前にも同じ相対ルートを使用してください。利点:変換する必要がありません。デメリット:異なるディレクトリのcssシートを1つのバンドルにまとめることには制限があります。
1
Kovács Ede

CssRewriteUrlTransformが私の問題を修正しました。
CssRewriteUrlTransformを使用してもコードがまだ画像をロードしない場合は、CSSファイル名を次のように変更します。

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

に:

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

とにかく、(ドット)はURLで認識されていません。

0