开发者

Referencing images after combing css files

I'm developing an asp.net webforms website. Website structure:

/
    capabilities/
        capability-A/
            style.css
            images/
                imageA.png
                imageB.png
        capability-B/
            style.css
            images/
                im开发者_运维百科ageC.png
                imageD.png
        ...

On release build all the style.css files are combined and put in /style.css file. Because they may reference images in corresponding capability/images folder (using relative paths) all the images are copied into /images folder.

On debug build the structure does not change (css files are not combined, etc).

The problem araises when I try to reference those images on pages - the path to images differs in debug and release builds (like /capabilities/capability-A/images/foo.png for debug and /images/foo.png for release).

One really bad idea i thought of was to check HttpContext.Current.IsDebuggingEnabled and put different image path each time.

Another one - reference all images using absolute paths and not to copy images to /images folder. With this approach I won't be able to rename/move css anywhere without changing it's contents.

Third one is to always reference debug-version path and on each release build replace those debug-version paths with correct ones (but I hope there's a simpler approach).

How would you solve this referencing images issue after combining css files?


Why not use some helper function to output the image path - the helper function can take the site relative path of image and output it as is when debug build (using conditional statements #if) and collapse the path for release builds. For example,

public string GetImagePath(string url)
{
   #if DEBUG
      return "/images/" + url;
   #else
      return "/images/" + url.Substring(url.LastIndexOf(”/”) + 1);
   #endif
}

Use it such as

<img src='<%= GetImagePath("/capabilities/capability-A/images/foo.png")  %>'

EDIT: yet another alternative is to serve all images via an simple http-handler such as

<%@ WebHandler Language="C#" Class="ImageHandler" %>

using System;
using System.Web;

public class ImageHandler : IHttpHandler {

    public void ProcessRequest (HttpContext context) {
       // get relative image path (or have some key to image mapping logic)
       var path = request.QueryString["q"]; 

       // map image path
       #if DEBUG

       #else
          path = System.IO.Path.GetFileName(path);
       #endif

       // get the physical path
       path = Path.Combine(context.Server.MapPath("~/Images"), path);

       // Emit cache headers (recommended)
       ...

       // transmit the image file
       context.Response.TransmitFile(path);
    }
}


Ok, I found a solution. Instead of copying images and replacing paths in pages on release builds I decided to leave images in their folders and replace relative paths in css files with absolute paths. Replacing occurs before combining files so that replacer-script could have information about where the css file is located to generate absolute path. I use web deployment project with Script and FileUpdate tasks from msbuild community tasks to achieve this. Script task is used to create absolute url portion that needs to be inserted in front of each relative path. FileUpdate task is used to replace all relative paths with their absolute analogs using regex.

Here's part of the msbuild script:

<Target Name="BeforeBuild">
    <CallTarget Targets="CombineAndMinifyCssAndJavaScript" />
</Target>

<Target Name="_InitCombineAndMinifyCssAndJavaScript">
    <ItemGroup>
        <!-- These are the CSS files that will be combined. -->
        <InputCssFiles Include="$(CopyBeforeBuildTargetPath)\capabilities\**\*.css" />
    </ItemGroup>
</Target>

<Target Name="_CorrectResourceUrlsInCSSFiles"
        Outputs="%(InputCssFiles.Identity)"
        DependsOnTargets="_InitCombineAndMinifyCssAndJavaScript"
        Condition=" '@(InputCssFiles)' != '' ">
    <PropertyGroup>
        <InputCssFileDir>%(InputCssFiles.RootDir)%(InputCssFiles.Directory)</InputCssFileDir>
        <CodeToExtractAbsoluteUrlPath>
            <![CDATA[
                public static String ScriptMain()
                {
                    String cssDir = @"$(InputCssFileDir)".ToLowerInvariant();
                    String websiteDir = @"$(CopyBeforeBuildTargetPath)".ToLowerInvariant();

                    cssDir = cssDir.TrimEnd(new char[] { '\\' });
                    websiteDir = websiteDir.TrimEnd(new char[] { '\\' });

                    if (!cssDir.StartsWith(websiteDir))
                    {
                        throw new ArgumentException(String.Format(
                            "CSS file directory ({0}) should be somewhere under website root ({1}).", cssDir, websiteDir));
                    }

                    String absoluteUrlPath = (cssDir.Remove(0, websiteDir.Length) + "\\").Replace('\\', '/');
                    return absoluteUrlPath;
                }
            ]]>
        </CodeToExtractAbsoluteUrlPath>
    </PropertyGroup>

    <Script Language="C#" Code="$(CodeToExtractAbsoluteUrlPath)">
        <Output PropertyName="AbsoluteUrlPath" TaskParameter="ReturnValue" />
    </Script>

    <!--
        Regex is:
            url \s*
            \( \s*
                ['"]
                    ([^/'":][^'":]*)
                ['"]
            \s* \)
    -->
    <FileUpdate
        Files="%(InputCssFiles.FullPath)"
        Regex="url\s*\(\s*[&apos;&quot;]([^/&apos;&quot;:][^&apos;&quot;:]*)[&apos;&quot;]\s*\)"
        ReplacementText="url('$(AbsoluteUrlPath)$1')" />
</Target>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜