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