MVC4 StyleBundle छवियों को हल नहीं कर रहा है


293

मेरा प्रश्न इसके समान है:

ASP.NET MVC 4 न्यूनतमकरण और पृष्ठभूमि छवियां

सिवाय इसके कि अगर मैं कर सकता हूं तो मैं एमवीसी के अपने बंडलिंग के साथ रहना चाहता हूं। मैं एक मस्तिष्क दुर्घटना का पता लगाने की कोशिश कर रहा हूं कि स्टाइल बंडलों को निर्दिष्ट करने के लिए सही पैटर्न क्या है जैसे कि स्टैंडअलोन सीएसएस और छवि सेट जैसे कि jQuery UI काम।

मेरे पास एक सामान्य एमवीसी साइट संरचना है /Content/css/जिसमें मेरा आधार सीएसएस जैसे है styles.css। उस सीएसएस फोल्डर के भीतर मेरे पास सबफ़ोल्डर्स भी हैं जैसे कि /jquery-uiइसकी सीएसएस फ़ाइल प्लस एक /imagesफ़ोल्डर है। JQuery UI CSS में छवि पथ उस फ़ोल्डर के सापेक्ष हैं और मैं उनके साथ गड़बड़ नहीं करना चाहता।

जैसा कि मैं इसे समझता हूं, जब मैं निर्दिष्ट करता StyleBundleहूं कि मुझे एक आभासी पथ निर्दिष्ट करने की आवश्यकता है जो एक वास्तविक सामग्री पथ से मेल नहीं खाता है, क्योंकि (यह मानते हुए कि मैं सामग्री के लिए मार्गों की अनदेखी कर रहा हूं) IIS तब उस पथ को भौतिक फ़ाइल के रूप में हल करने का प्रयास करेगा। तो मैं निर्दिष्ट कर रहा हूँ:

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

का उपयोग कर गाया:

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

मैं बाहर जाने के अनुरोध को देख सकता हूं:

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

यह सही, मिनिमाइल्ड CSS प्रतिक्रिया लौटा रहा है। लेकिन तब ब्राउज़र अपेक्षाकृत लिंक की गई छवि के लिए अनुरोध भेजता है:

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

जो ए 404

मैं समझता हूं कि मेरे URL jquery-uiका अंतिम भाग एक एक्सटेंशन रहित URL है, जो मेरे बंडल के लिए एक हैंडलर है, इसलिए मैं देख सकता हूं कि छवि के लिए सापेक्ष अनुरोध बस क्यों है /styles/images/

तो मेरा सवाल यह है कि इस स्थिति को संभालने का सही तरीका क्या है ?


9
नए बंडलिंग और न्यूनतम भाग के साथ बार-बार निराश होने के बाद , मैं कैसेट चुड़ैल पर चला गया अब स्वतंत्र है और बेहतर काम करता है!
बेलेकांड्रे

3
लिंक के लिए धन्यवाद, कैसेट अच्छा लग रहा है और मैं निश्चित रूप से इसे देखूंगा। लेकिन मैं यदि संभव हो तो प्रदान किए गए दृष्टिकोण के साथ रहना चाहता हूं, निश्चित रूप से यह संभव है कि हर बार एक नया संस्करण जारी होने पर 3 पार्टी सीएसएस फाइलों में छवि पथों के साथ खिलवाड़ किए बिना संभव हो। अभी के लिए मैंने अपना ScriptBundles (जो अच्छी तरह से काम करता है) रखा है, लेकिन जब तक मुझे रिज़ॉल्यूशन नहीं मिलता, सादे सीएसएस लिंक पर वापस आ जाता है। चीयर्स।
टॉम डब्ल्यू हॉल

एसईओ कारणों के लिए संभावित त्रुटि को जोड़ना: पथ के लिए नियंत्रक '/bundles/images/blah.jpg' नहीं मिला या IController को लागू नहीं करता है।
ल्यूक पुप्लेट

जवाबों:


361

MVC4 सीएसएस बंडलिंग और छवि संदर्भों पर इस धागे के अनुसार , यदि आप अपने बंडल को इस प्रकार परिभाषित करते हैं:

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

जहाँ आप बंडल को उसी रास्ते पर परिभाषित करते हैं जैसे स्रोत फ़ाइलें जो बंडल बनाती हैं, सापेक्ष छवि पथ अभी भी काम करेंगे। बंडल पथ का अंतिम भाग वास्तव में file nameउस विशिष्ट बंडल के लिए है (यानी, /bundleआप जैसा भी नाम रख सकते हैं)।

यह केवल तभी काम करेगा जब आप उसी फ़ोल्डर से CSS (जो मुझे लगता है कि एक बंडल परिप्रेक्ष्य से समझ में आता है) को एक साथ बंडल कर रहे हैं।

अपडेट करें

@ हाओ कुंग द्वारा नीचे की टिप्पणी के अनुसार, वैकल्पिक रूप से अब इसे CssRewriteUrlTransformation( बंडल किए जाने पर सीएसएस फ़ाइलों के सापेक्ष URL संदर्भों को बदलकर) प्राप्त किया जा सकता है ।

नोट: मैंने किसी वर्चुअल निर्देशिका के भीतर निरपेक्ष पथों पर पुनर्लेखन के साथ मुद्दों के बारे में टिप्पणियों की पुष्टि नहीं की है, इसलिए यह हर किसी के लिए काम नहीं कर सकता (।)।

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

1
पौराणिक कथा! हां, यह पूरी तरह से काम करता है। मेरे पास अलग-अलग स्तरों पर CSS हैं, लेकिन उनमें से प्रत्येक के पास अपने स्वयं के चित्र फ़ोल्डर हैं, उदाहरण के लिए मेरी मुख्य साइट CSS रूट CSS फ़ोल्डर में है और फिर jquery-ui इसके अंदर है कि अपने स्वयं के चित्र फ़ोल्डर के साथ है, इसलिए मैं सिर्फ 2 बंडलों को निर्दिष्ट करता हूं, मेरे लिए एक आधार सीएसएस और jQuery UI के लिए एक - जो अनुरोधों के संदर्भ में शायद uber- इष्टतम नहीं है, लेकिन जीवन छोटा है। चीयर्स!
टॉम डब्ल्यू हॉल

3
दुर्भाग्य से जब तक बंडल में सीएसएस के अंदर एम्बेडेड यूआरएल को फिर से लिखने के लिए समर्थन नहीं है, तब तक आपको बंडल करने से पहले सीएसएस फ़ाइलों से मिलान करने के लिए सीएसएस बंडल की आभासी निर्देशिका की आवश्यकता होती है। यही कारण है कि डिफ़ॉल्ट टेम्पलेट बंडलों में ~ / बंडलों / थीम जैसे यूआरएल नहीं हैं, और इसके बजाय निर्देशिका संरचना की तरह दिखते हैं: ~ / सामग्री / थीम्स / आधार / सीएसएस
हाओ कुंग

27
यह अब ItemTransforms, .Include ("~ / सामग्री / css / jquery-ui / *। Css", नए CssRewriteUrlTransform ())) के माध्यम से समर्थित है; 1.1B1 में इस मुद्दे को हल करना चाहिए
हाओ कुंग

2
क्या यह Microsoft ASP.NET वेब ऑप्टिमाइज़ेशन फ्रेमवर्क 1.1.3 में तय किया गया है? मुझे इस बात में कोई जानकारी नहीं मिली कि इसमें क्या बदला है?
एंड्रस

13
यदि आपके पास IIS में कोई वेबसाइट है तो नया CssRewriteUrlTransform () ठीक है। लेकिन अगर इसका एप्लिकेशन या सब एप्लिकेशन यह काम नहीं करेगा, और आपको सीएसएस के रूप में उसी स्थान पर अपने बंडल को परिभाषित करना होगा।
एविएनिक जूल 15'14

34

ग्रिन / ThePirat समाधान अच्छी तरह से काम करता है।

मुझे यह पसंद नहीं आया कि यह बंडल पर नई विधि को शामिल कर ले, और इसने सामग्री निर्देशिका में अस्थायी फ़ाइलें बनाईं। (वे समाप्त हो गया, जाँच में तैनात किया गया, फिर सेवा शुरू नहीं होगी!)

इसलिए बंडलिंग के डिजाइन का पालन करने के लिए, मैं अनिवार्य रूप से एक ही कोड का चयन करने के लिए चुना, लेकिन एक 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);
            }
        }
    }
}

और फिर इसे एक बंडल इम्प्लिमेंटेशन में लपेट दिया:

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

यह मुझे भी साफ लगता है। धन्यवाद। मैं आप तीनों को वोट दे रहा हूं क्योंकि यह एक टीम प्रयास था। :)
जोश मोच

आपके पास यह कोड अब मेरे लिए काम नहीं कर रहा है। मैं इसे ठीक करने की कोशिश कर रहा हूं, लेकिन मुझे लगा कि मैं आपको बता दूंगा। संदर्भ .ttpContext.RelativeFromAbsolutePath विधि मौजूद नहीं है। इसके अलावा, यदि url पथ एक "/" (इसे पूर्ण बनाते हुए) से शुरू होता है, तो आपका तर्क संयोजन मार्ग बंद है।
जोश मच

2
@AcidPAT महान कार्य। तर्क विफल हो गया अगर url में एक querystring था (कुछ 3rd पार्टी पुस्तकालय इसे जोड़ते हैं, जैसे कि .woff अपने .woff संदर्भ के लिए।) हालांकि यह एक आसान समाधान है। एक Regex को समायोजित कर सकता है या relativeToCSSकॉल करने से पहले ठीक कर सकता है Path.GetFullPath()
sergiopereira

2
@ChrisMarisic आपका कोड काम नहीं करता है - response.Files, बंडलफाइल्स की एक सरणी है, इन ऑब्जेक्ट में "Exists", "DirectoryName" आदि जैसे गुण नहीं हैं
Nick Coad

2
@ क्रिसमिसिसिक शायद एक ऐसा नामस्थान है जिसे मुझे आयात करना चाहिए जो बंडलफाइल क्लास के लिए विस्तार के तरीके प्रदान करता है?
निक कॉड

20

बेहतर अभी तक (IMHO) एक कस्टम बंडल लागू करता है जो छवि पथ को ठीक करता है। मैंने अपने ऐप के लिए एक लिखा है।

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 और base64 DataURIs को ध्यान में रखते हुए सुनिश्चित किया है कि URL पथ में कोई कॉलन (:) नहीं है। हमारे वातावरण में, छवियां समान रूप से उनके सीएसएस फ़ाइलों के समान फ़ोल्डर में रहती हैं, लेकिन मैंने इसे मूल फ़ोल्डर ( url(../someFile.png)) और चाइल्ड फ़ोल्डर ( url(someFolder/someFile.png) दोनों के साथ परीक्षण किया है ।


यह एक बेहतरीन उपाय है। मैंने आपके रेगेक्स को थोड़ा संशोधित किया ताकि यह LESS फाइलों के साथ भी काम करे, लेकिन मूल अवधारणा बिल्कुल वही थी जिसकी मुझे आवश्यकता थी। धन्यवाद।
टिम कुल्टर

आप लूप के बाहर भी रेगेक्स इनिशियलाइज़ेशन डाल सकते हैं। शायद एक स्थैतिक आसानी से संपत्ति के रूप में।
मिहा मार्किक

12

किसी ट्रांसफ़ॉर्म को निर्दिष्ट करना या पागल उपनिर्देशिका पथ होना आवश्यक नहीं है। बहुत समस्या निवारण के बाद मैंने इसे इस "सरल" नियम से अलग कर दिया (क्या यह बग है?) ...

यदि आपका बंडल पथ शामिल किए जा रहे आइटम के सापेक्ष रूट से शुरू नहीं होता है, तो वेब एप्लिकेशन रूट को ध्यान में नहीं रखा जाएगा।

मेरे लिए एक बग की तरह लगता है, लेकिन वैसे भी आप इसे वर्तमान .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")

और "व्यवहार" (बग) प्राप्त करें, सीएसएस फाइलों में स्वयं आवेदन रूट होता है (उदाहरण के लिए "http: // localhost: 1234 / MySite / Content / Site.css"), लेकिन सभी प्रारंभ के भीतर CSS छवि "/ सामग्री / चित्र / ... "या" / छवियां / ... "इस पर निर्भर करता है कि मैं रूपांतरण जोड़ता हूं या नहीं।

यहां तक ​​कि "बंडल" फ़ोल्डर बनाने की कोशिश की गई यह देखने के लिए कि क्या यह मौजूदा पथ के साथ करना है या नहीं, लेकिन इससे कुछ भी नहीं बदला। समस्या का समाधान वास्तव में आवश्यकता है कि बंडल का नाम पथ रूट से शुरू होना चाहिए।

मतलब यह उदाहरण बंडल मार्ग को पंजीकृत करने और प्रस्तुत करने के द्वारा तय किया गया है जैसे ..

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

तो बेशक आप यह कह सकते हैं कि यह आरटीएफएम है, लेकिन मुझे पूरा यकीन है और अन्य लोगों ने इसे डिफ़ॉल्ट रूप से या MSDN या ASP.NET वेब साइट पर प्रलेखन में कहीं और "~ / बंडल / ..." पथ से उठाया है, या सिर्फ इस पर ठोकर खाई क्योंकि वास्तव में यह एक आभासी पथ के लिए काफी तार्किक नाम है और ऐसे आभासी रास्तों को चुनने के लिए समझ में आता है जो वास्तविक निर्देशिकाओं के लिए संघर्ष नहीं करते हैं।

वैसे भी, यह तरीका है। Microsoft बग नहीं देखें मैं इससे सहमत नहीं हूं, या तो इसे उम्मीद के मुताबिक काम करना चाहिए या कुछ अपवाद को फेंक दिया जाना चाहिए, या बंडल रूट को जोड़ने के लिए एक अतिरिक्त ओवरराइड करना चाहिए जो कि एप्लिकेशन रूट को शामिल करने का विकल्प है या नहीं। मैं कल्पना नहीं कर सकता कि कोई क्यों नहीं चाहेगा कि जब कोई एक (सामान्य रूप से जब तक आप अपनी वेबसाइट को डीएनएस उर्फ ​​/ डिफॉल्ट वेब साइट रूट के साथ स्थापित न करें) में शामिल हैं। तो वास्तव में वैसे भी डिफ़ॉल्ट होना चाहिए।


मुझे सबसे सरल "समाधान" लगता है। दूसरों के दुष्प्रभाव हो सकते हैं, जैसे छवि: डेटा।
फेब्रिस

@ मोहम्मदईश यह काम करता है, आपको शायद कुछ गलत लगा। अनुरोधों का पता लगाना सीखें, जैसे कि ब्राउज़र द्वारा अनुरोध किए जा रहे URL को देखने के लिए Fiddler Tool का उपयोग करें। लक्ष्य पूरे रिश्तेदार पथ को हार्ड-कोड करना नहीं है, इसलिए आपकी वेब साइट को एक ही सर्वर पर अलग-अलग स्थानों (रूट पथ) में स्थापित किया जा सकता है या आपका उत्पाद बहुत सारे वेब साइट को फिर से लिखने के बिना डिफ़ॉल्ट URL को बदल सकता है। (रूट चर और होने का बिंदु)।
टोनी वॉल

इस विकल्प के साथ गए और इसने बहुत अच्छा काम किया। यह सुनिश्चित करने के लिए कि प्रत्येक बंडल में केवल एकल फ़ोल्डर से आइटम थे (अन्य फ़ोल्डर या सबफ़ोल्डर से आइटम शामिल नहीं कर सकते हैं), जो थोड़ा कष्टप्रद है लेकिन जब तक यह काम करता है मैं खुश हूं! पोस्ट के लिए धन्यवाद।
hugughan3

1
धन्यवाद। आह। किसी दिन मैं वास्तव में ब्राउज़िंग स्टैक की तुलना में कोड लिखने में अधिक समय बिताना चाहूंगा।
ब्रूस पीरसन

मेरे पास एक समान मुद्दा था जहां एक कस्टम jquery-ui जिसमें नेस्टेड फ़ोल्डर्स थे। जैसे ही मैंने ऊपर की चीजों को समतल किया, उसने काम किया। यह नेस्टेड फ़ोल्डर पसंद नहीं है।
आंद्रेई बाजानोव

11

मैंने पाया कि CssRewriteUrlTransform चलाने में विफल रहता है यदि आप किसी *.cssफ़ाइल का संदर्भ ले रहे हैं और आपके पास *.min.cssउसी फ़ोल्डर में संबंधित फ़ाइल है।

इसे ठीक करने के लिए, या तो *.min.cssफ़ाइल को हटा दें या इसे सीधे अपने बंडल में देखें:

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

उसके बाद आप ऐसा करते हैं, आपके URL सही तरीके से रूपांतरित हो जाएंगे और आपकी छवियां सही ढंग से हल हो जानी चाहिए।


1
धन्यवाद! ऑनलाइन खोज करने के दो दिनों के बाद, यह पहला उल्लेख है जिसे मैंने कहीं भी CssRewriteUrlTransform के साथ * .css फ़ाइलों के साथ काम करते हुए देखा है, लेकिन संबंधित * .min.css फ़ाइल के साथ नहीं जब आप डिबग में नहीं चल रहे होते हैं। वातावरण। निश्चित रूप से मेरे लिए एक बग की तरह लगता है। डिबगिंग के लिए अनमनीकृत संस्करण के साथ एक बंडल को परिभाषित करने के लिए मैन्युअल रूप से पर्यावरण प्रकार की जांच करनी होगी, लेकिन कम से कम मेरे पास अब एक समाधान है!
शॉन

1
इससे मेरे लिए समस्या ठीक हो गई। यह निश्चित रूप से एक बग की तरह लगता है। इसका कोई मतलब नहीं है कि यह CssRewriteUrlTransform को अनदेखा करना चाहिए अगर यह पहले से मौजूद .min.css फ़ाइल को ढूंढता है।
user1751825

10

शायद मैं पक्षपाती हूं, लेकिन मुझे अपना समाधान काफी पसंद है क्योंकि यह कोई भी रूपांतरण नहीं करता है, रेगेक्स आदि और यह कम से कम पास है :)

यह IIS वेब साइट में वर्चुअल निर्देशिका के रूप में होस्ट की गई साइट और IIS पर रूट वेबसाइट के रूप में काम करता है

की तो मैं एक Implentation बनाया IItemTransformसमझाया CssRewriteUrlTransformऔर इस्तेमाल किया VirtualPathUtilityपथ को ठीक करने और मौजूदा कोड कॉल करने के लिए:

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

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

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


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

मेरे लिए ठीक काम करने लगता है?


1
यह पूरी तरह से मेरे लिए सूट है। उत्कृष्ट समाधान। मेरा वोट +1 है
imdadhusen

1
यह सही जवाब है। फ्रेमवर्क द्वारा प्रदान की गई CssUrlTransformWrapper वर्ग समस्या को संबोधित करता है, सिवाय इसके कि यह केवल तभी काम नहीं करता जब एप्लिकेशन वेब साइट रूट पर न हो। यह आवरण स्पष्ट रूप से उस कमी को संबोधित करता है।
नौ

7

यद्यपि क्रिस बैक्सटर का उत्तर मूल समस्या के साथ मदद करता है, यह मेरे मामले में काम नहीं करता है जब एप्लिकेशन को वर्चुअल निर्देशिका में होस्ट किया जाता है । विकल्पों की जांच करने के बाद, मैं DIY समाधान के साथ समाप्त हुआ।

ProperStyleBundleक्लास में CssRewriteUrlTransformवर्चुअल निर्देशिका के भीतर रिश्तेदार रास्तों को ठीक से बदलने के लिए मूल से उधार लिया गया कोड शामिल है । यदि फ़ाइल मौजूद नहीं है, तो यह भी फेंकता है और बंडल में फ़ाइलों के पुन: व्यवस्थित होने से रोकता है (से लिया गया कोड BetterStyleBundle)।

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

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

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

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

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

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

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

        private class ProperCssRewriteUrlTransform : IItemTransform
        {
            private readonly string _basePath;

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

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

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

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

इसका उपयोग करें जैसे StyleBundle:

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

2
अच्छा समाधान है, लेकिन अभी भी विफल रहता है (बस CssRewriteUrlTransform की तरह) यदि आपके पास अपने CSS में एक डेटा URI है (उदाहरण के लिए "डेटा: छवि / png; base64, ...")। आपको RebaseUrlToAbsolute () में "डेटा:" के साथ url की शुरुआत को नहीं बदलना चाहिए।
14:82 पर मील 82

1
@ मील 82 बेशक! इस पर ध्यान दिलाने के लिए धन्यवाद। मैंने RebaseUrlToAbsolute () बदल दिया है।
नादिक

6

V1.1.0-अल्फा 1 (प्री रिलीज पैकेज) के रूप में, फ्रेम 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);
            }
        }
    }
}

वास्तव में, यह क्या करता है अगर सीएसएस में सापेक्ष URL को निरपेक्ष द्वारा प्रतिस्थापित किया जाए।
फेब्रीस

6

यहाँ एक बंडल ट्रांसफ़ॉर्म है जो उस सीएसएस फ़ाइल के सापेक्ष 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);
    }

}

इसका उपयोग कैसे करें?, यह मुझे एक अपवाद दिखाएगा:cannot convert type from BundleFile to FileInfo
स्टीमर

@Stiger परिवर्तन css.FullName.Replace (css.VirtualFile.VirtualPath.Replace पर
lkurylo

मैं इस गलत का उपयोग कर सकता हूं, लेकिन क्या वह फ़ॉरचेक हर पुनरावृत्ति पर सभी url को फिर से लिखता है और अंतिम सीएसएस फ़ाइल के सापेक्ष उन्हें छोड़ देता है जो उसने देखा था?
एंड्रियोगर

4

एक अन्य विकल्प यह होगा कि आईआईएस यूआरएल रिवर्ट मॉड्यूल का उपयोग करके वर्चुअल बंडल इमेज फोल्डर को फिजिकल इमेज फोल्डर में मैप करें। नीचे एक पुनर्लेखन नियम का एक उदाहरण दिया गया है, जिसका उपयोग आप "~ / बंडलों / अपने पृष्ठ / शैलियों" नामक बंडल के लिए कर सकते हैं - अल्फ़ान्यूमेरिक वर्णों के साथ-साथ हाइफ़न, अंडरस्कोर और अवधियों पर रेगेक्स मिलान पर ध्यान दें, जो छवि फ़ाइल नामों में सामान्य हैं ।

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

यह दृष्टिकोण थोड़ा अतिरिक्त ओवरहेड बनाता है, लेकिन आपको अपने बंडल नामों पर अधिक नियंत्रण रखने की अनुमति देता है, और एक पृष्ठ पर आपके द्वारा किए जा सकने वाले बंडल की संख्या को भी कम करता है। बेशक, यदि आपको कई 3 पार्टी सीएसएस फ़ाइलों को संदर्भित करना है, जिसमें सापेक्ष छवि पथ संदर्भ हैं, तो आप अभी भी कई बंडल बनाने के आसपास नहीं पहुंच सकते हैं।


4

ग्रिन समाधान महान है।

हालाँकि, यह मेरे लिए तब काम नहीं करता जब url में पैरेंट फोल्डर रिलेटिव रेफरेंस हों। अर्थातurl('../../images/car.png')

इसलिए, मैंने Includeप्रत्येक रेगेक्स मैच के लिए पथों को हल करने के लिए विधि को थोड़ा बदल दिया , रिश्तेदार पथों की अनुमति दी और वैकल्पिक रूप से सीएसएस में छवियों को एम्बेड करने के लिए।

मैंने BundleTable.EnableOptimizationsइसके बजाय IF DEBUG को चेक करने के लिए भी बदल दिया 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;
    }

आशा है कि यह मदद करता है, का संबंध है।


2

आप बस अपने आभासी बंडल पथ में गहराई का एक और स्तर जोड़ सकते हैं

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

यह एक सुपर कम तकनीक का जवाब है और एक हैक की तरह है, लेकिन यह काम करता है और किसी भी पूर्व प्रसंस्करण की आवश्यकता नहीं होगी। इनमें से कुछ उत्तरों की लंबाई और जटिलता को देखते हुए मैं इसे इस तरह से करना पसंद करता हूं।


जब आप अपना वेब ऐप IIS में वर्चुअल एप्लिकेशन के रूप में रखते हैं तो यह मदद नहीं करता है। मेरा मतलब है कि यह काम कर सकता है लेकिन आपको अपने कोड के रूप में अपने IIS वर्चुअल ऐप का नाम देना चाहिए, जो कि आप नहीं चाहते हैं, ठीक है?
प्यूलसक

जब आईआईएस में वर्चुअल एप्लिकेशन होता है तो मुझे भी यही समस्या होती है। यह उत्तर मेरी मदद करता है।
बिल

2

मुझे बंडलों के साथ यह समस्या थी कि गलत रास्तों की छवियों के साथ और 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)


मेरे पास String.Count () ओवरलोड नहीं है जो एक स्ट्रिंग को स्वीकार करता है ( m.Groups[2].Value.Count("..")काम नहीं करता है।) और Value.StartsWith('/')या तो काम नहीं करता है क्योंकि StartsWith एक char के बजाय एक स्ट्रिंग की उम्मीद करता है।
जौ १३'१४

@ जओ मेरे बुरे मैंने इसे साकार किए बिना कोड में अपने स्वयं के विस्तार के तरीकों को शामिल किया।
जाहू

1
@ जाओ ने उन विस्तार विधियों के स्रोत कोड को उत्तर में जोड़ा।
जाहू

1

थोड़ी जांच के बाद मैंने निष्कर्ष निकाला: आपके पास 2 विकल्प हैं:

  1. परिवर्तनों के साथ जाओ। इसके लिए बहुत उपयोगी पैकेज: https://bundletransformer.codeplex.com/ आपको हर समस्याग्रस्त बंडल के लिए निम्नलिखित परिवर्तन की आवश्यकता है:

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

लाभ: इस समाधान के लिए, आप अपने बंडल को जो चाहें नाम दे सकते हैं => आप विभिन्न निर्देशिकाओं से एक बंडल में सीएसएस फ़ाइलों को जोड़ सकते हैं। नुकसान: आपको प्रत्येक समस्याग्रस्त बंडल को बदलने की आवश्यकता है

  1. बंडल के नाम के लिए उसी रिश्तेदार रूट का उपयोग करें जैसे कि css फ़ाइल स्थित है। लाभ: परिवर्तन की कोई आवश्यकता नहीं है। नुकसान: आपके पास अलग-अलग निर्देशिकाओं से सीएसएस शीट्स को एक बंडल में संयोजित करने की सीमा है।

0

CssRewriteUrlTransformमेरी समस्या ठीक कर दी।
यदि आपका कोड अभी भी उपयोग करने के बाद छवियों को लोड नहीं कर रहा है CssRewriteUrlTransform, तो अपने सीएसएस फ़ाइल नाम से बदलें:

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

सेवा:

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

कहीं। (डॉट्स) यूआरएल में नहीं पहचान रहे हैं।


0

बस एक बंडल में कई सीएसएस समावेशन ठीक करने के लिए याद रखें जैसे:

bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
    .Include("~/Content/css/path1/somestyle1.css", "~/Content/css/path2/somestyle2.css"));

आप बस new CssRewriteUrlTransform()एक सीएसएस फ़ाइल के साथ अंत में जोड़ नहीं सकते क्योंकि विधि इसका समर्थन नहीं करती है, इसलिए आपको कई बार उपयोग Includeकरना होगा :

bundles.Add(new StyleBundle("~/Content/styles/jquery-ui")
    .Include("~/Content/css/path1/somestyle1.css", new CssRewriteUrlTransform())
    .Include("~/Content/css/path2/somestyle2.css", new CssRewriteUrlTransform()));
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.