使用Dotless和.NET C#实现自定义CSS文件下载

在本系列文章中,将向用户提供一种下载他们创建的CSS文件的方法。将使用几种技术,这些技术本身也非常有用。首先,将使用Dotless库来转换less文件。如果已经阅读了之前的系列文章,那么需要理解现在转向使用.NETC#

文章索引:

  • 第1部分(设置UI)
  • 第2部分(解析variables.less)
  • 第3部分(UI细化)
  • 第4部分(生成custom-bootstrap.css)
  • 演示

DotlessClientOnly nuGet

已经安装了DotlessClientOnly nuGet包。这很重要!不要安装完整包,因为它会在每次请求时转换.less文件,从而阻塞UI解析它。

文件内容结果动作

在HomeController中,将添加一个名为GetCss的动作,并将其设置为FileContentResult类型。HomeController位于路径"Controllers/HomeController.cs"。

public FileContentResult GetCss() { var contentType = "text/css"; var content = "body { background-color : #fff; }"; var bytes = Encoding.UTF8.GetBytes(content); var result = new FileContentResult(bytes, contentType); return result; }

如果运行网站并访问路径Home/GetCss,应该得到以下输出:

CSS body { background-color : #fff; }

解析bootstrap.less文件

现在,首先忽略用户通过UI创建的变量。如果它能解析默认的bootstrap.less文件,就很高兴了。

所以让利用Dotless类:

public FileContentResult GetCss() { // 设置内容类型 var contentType = "text/css"; // 获取bootstrap.less内容 var bootstrapLessContent = System.IO.File.ReadAllText( HostingEnvironment.MapPath("~/Content/bootstrap/bootstrap.less") ); // 创建Dotless配置 var config = new DotlessConfiguration(); config.MinifyOutput = false; config.ImportAllFilesAsLess = true; config.CacheEnabled = false; // 解析.less文件 var content = Less.Parse(bootstrapLessContent, config); var bytes = Encoding.UTF8.GetBytes(content); var result = new FileContentResult(bytes, contentType); return result; }

测试

如果正确解决了所有引用,这应该会编译。但如果运行它,它会抛出一个异常:

这是因为在bootstrap.less中,对其他文件的导入没有设置路径。所以Dotless类不知道如何找到它们。

IFileReader

幸运的是,dotless库为提供了一个解决方案。可以决定它实际上是如何找到文件的。这是通过实现IFileReader接口来完成的。这很棒,因为以后可以利用这个来替换自己的变量。

但首先,将尝试输出标准的.css。需要创建一个类。在子文件夹classes中这样做了,将其命名为VirtualFileReader。类应该如下所示:

internal sealed class VirtualFileReader : IFileReader { public byte[] GetBinaryFileContents(string fileName) { fileName = GetFullPath(fileName); return File.ReadAllBytes(fileName); } public string GetFileContents(string fileName) { fileName = GetFullPath(fileName); return File.ReadAllText(fileName); } public bool DoesFileExist(string fileName) { fileName = GetFullPath(fileName); return File.Exists(fileName); } private static string GetFullPath(string path) { return HostingEnvironment.MapPath("~/Content/bootstrap/" + path); } public bool UseCacheDependencies { get { return true; } } }

接下来,需要DotlessConfiguration使用这个类。所以在GetCss动作中,向config添加这行代码:

config.LessSource = typeof(VirtualFileReader);

测试

运行网站并访问路径:/home/getcss。现在应该得到一个编译的CSS文件:

CSS /* ! normalize.css v3.0.2 | MIT License | git.io/normalize */ html { font-family: sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; } body { margin: 0; } article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, ... and so on.

保存变量

在本节中,将回到JavaScript,将变量发送到服务器。但首先,将在HomeController中再创建一个动作,将其存储在Session变量中。

[HttpPost] public EmptyResult SendVariables() { Stream req = Request.InputStream; req.Seek(0, System.IO.SeekOrigin.Begin); string variables = new StreamReader(req).ReadToEnd(); HttpContext.Session["variables.less"] = variables; return new EmptyResult(); }

现在,需要回到JavaScript。基本上,将创建一个函数,从viewModel重写variables.less文件。所以在main.js中,将创建一个sendVariables函数,它将遍历类别、它们的变量并重写variables.less文件。

但首先,将添加一些其他东西。在_Layout.cshtml文件中,向主菜单栏添加一个动作链接。如下所示:

<div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("Elements", "Elements", "Home")</li> <li>@Html.ActionLink("Contact", "Contact", "Home")</li> <li> <a href="#" data-bind="click: sendAndReturnVariablesFile">Get the css</a> </li> </ul> </div> @Html.Partial("_LoginPartial")

希望用户能够将此文件作为下载。这不可能通过Ajax实现,将他们发送到一个空白页面也是不可接受的。所以将设置一个不可见的iframe。

<iframe id="file-download" class="hidden"></iframe>

这可以放在页面的任何地方。但在页面底部,在body关闭标签之前似乎是最好的地方。

刚刚创建了绑定sendAndReturnVariables,所以需要将其添加到viewModel中。

/* * Recreates variables.less and sends it to the server * @function sendVariablesFile */ sendAndReturnVariablesFile: function() { var model = ko.toJS(viewModel), lessFileString = ""; // Recreate the variables.less content for (var prop in model.categories) { var category = model.categories[prop]; lessFileString += "//== " + prop + '\n'; for (var i = 0, j = category.variables.length; i < j; i++) { var varName = category.variables[i]; lessFileString += varName + ":" + model.variables[varName].value + ";\n"; } } // Send it to the server $.ajax({ url: "/Home/SendVariables", type: "POST", data: lessFileString, success: function() { // Set the iframe src to the css $("#file-download").attr("src", "/Home/GetCss"); } }); }

现在,需要在homeController的GetCss动作中添加一行代码。

var bytes = Encoding.UTF8.GetBytes(content); var result = new FileContentResult(bytes, contentType); result.FileDownloadName = "bootstrap-custom.css"; return result;

替换变量

所以现在需要执行最后一步。还记得VirtualFileReader吗?它有一个函数GetFileContents,将返回从Session保存的变量。

public string GetFileContents(string fileName) { if (fileName == "variables.less") { var variables = HttpContext.Current.Session["variables.less"]; return (string)variables; } else { fileName = GetFullPath(fileName); return File.ReadAllText(fileName); } }

结束(暂时)

自从写文章以来已经有一段时间了,但后悔了。非常喜欢创建这个系列,并且为真正产品学到了足够多的东西。希望有人能欣赏工作。

可能会围绕这个示例创建更多的文章。能想到一些其他可以做的事情。用户可以创建一个帐户,这将允许他们在数据库中存储多个版本。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485