Ссылки на изображения после очистки файлов css

#asp.net #css

#asp.net #css

Вопрос:

Я разрабатываю asp.net веб-сайт webforms. Структура веб-сайта:

 /
    capabilities/
        capability-A/
            style.css
            images/
                imageA.png
                imageB.png
        capability-B/
            style.css
            images/
                imageC.png
                imageD.png
        ...
  

При сборке релиза все style.css файлы объединяются и помещаются в /style.css файл. Поскольку они могут ссылаться на изображения в соответствующей capability/images папке (используя относительные пути), все изображения копируются в /images папку.

При отладочной сборке структура не изменяется (файлы css не объединяются и т.д.).

Проблема возникает, когда я пытаюсь ссылаться на эти изображения на страницах — путь к изображениям отличается в сборках debug и release (например, /capabilities/capability-A/images/foo.png для debug и /images/foo.png для release).

Одна действительно плохая идея, о которой я подумал, заключалась в том, чтобы каждый раз проверять HttpContext.Current.IsDebuggingEnabled и указывать другой путь к изображению.

Еще один — ссылайтесь на все изображения, используя абсолютные пути, а не копируйте изображения в /images папку. При таком подходе я не смогу переименовывать / перемещать css куда-либо без изменения его содержимого.

Третий — всегда ссылаться на путь к отладочной версии и при каждой сборке релиза заменять эти пути к отладочной версии правильными (но я надеюсь, что есть более простой подход).

Как бы вы решили эту проблему со ссылками на изображения после объединения файлов css?

Комментарии:

1. Можете ли вы показать пример того, как работает css, который ссылается на изображение?

2. @Louis: нравится .bg-add { background-image: url('images/add.png'); }

3. Для меня в сборках debug и release пути всегда указаны относительно местоположения файла css.

4. Когда вы говорите, что объединяете все файлы css, вы объединяете стили capability-A со стилями cabaility-B.

5. Да, стили capability-A и capability-B объединены. Итак, объединенный файл css находится в новом местоположении, вот почему возникают проблемы со ссылками.

Ответ №1:

Почему бы не использовать какую-нибудь вспомогательную функцию для вывода пути к изображению — вспомогательная функция может использовать относительный путь к сайту изображения и выводить его как есть при отладочной сборке (с использованием условных выражений #if ) и сворачивать путь для релизных сборок. Например,

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

Используйте это, например

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

РЕДАКТИРОВАТЬ: еще одна альтернатива — обслуживать все изображения с помощью простого http-обработчика, такого как

 <%@ 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);
    }
}
  

Комментарии:

1. Спасибо, это еще один способ. Но что мне в этом не нравится, так это то, что для серверных элементов управления мне пришлось бы указывать путь к изображению в codebehind.

2. @Loki, это правильно — одним из способов обхода является использование выражения привязки данных, такого как <%# GetImagePath(...) #%> — хитрость здесь заключается в вызове DataBind вызова на странице или подходящего элемента управления контейнером. Еще один способ — обслуживать все изображения через обработчик (ashx) — я отредактировал ответ, чтобы добавить элементарный код для того же самого.

Ответ №2:

Хорошо, я нашел решение. Вместо копирования изображений и замены путей на страницах при сборке релизов я решил оставить изображения в их папках и заменить относительные пути в файлах css абсолютными путями. Замена происходит перед объединением файлов, чтобы replacer-script мог иметь информацию о том, где находится файл css, для генерации абсолютного пути. Для достижения этой цели я использую проект веб-развертывания со скриптом и задачами FileUpdate из задач сообщества msbuild. Задача скрипта используется для создания абсолютной части URL, которую необходимо вставить перед каждым относительным путем. Задача FileUpdate используется для замены всех относительных путей их абсолютными аналогами с использованием регулярных выражений.

Вот часть скрипта msbuild:

 <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="urls*(s*[amp;apos;amp;quot;]([^/amp;apos;amp;quot;:][^amp;apos;amp;quot;:]*)[amp;apos;amp;quot;]s*)"
        ReplacementText="url('$(AbsoluteUrlPath)$1')" />
</Target>