在本系列文章中,将向用户提供一种下载他们创建的CSS文件的方法。将使用几种技术,这些技术本身也非常有用。首先,将使用Dotless库来转换less文件。如果已经阅读了之前的系列文章,那么需要理解现在转向使用.NETC#。
文章索引:
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);
}
}
结束(暂时)
自从写文章以来已经有一段时间了,但后悔了。非常喜欢创建这个系列,并且为真正产品学到了足够多的东西。希望有人能欣赏工作。
可能会围绕这个示例创建更多的文章。能想到一些其他可以做的事情。用户可以创建一个帐户,这将允许他们在数据库中存储多个版本。