之前有个业务需要根据用户的数据生成一张word的报表,
按照我之前的做法,
这种硬编码对于开发不是很友好,所以如果有一种面向对象编程,在编写过程中可以完全控制word的方法,岂不是可以减少很大的开发量?
这里我想到的办法是:
那么就需要后端针对这个静态页面进行绑定操作,这里微软提供了一种技术,
Razor 是一种允许您向网页中嵌入基于服务器的代码(Visual Basic 和 C#)的标记语法。
只需要将静态页面转换成cshtml页面(支持C#语法的解析和编写),这个很简单;然后通过在后端接口调用Engine.Razor来操作这个cshtml页面,实现数据绑定。
具体代码如下:
DetailProjectDto.cs
using RunGo.ToolsAttribute;
using System;
using System.ComponentModel.DataAnnotations;
namespace RunGo.ProjectsManager
{
/// <summary>
/// 工程详细实体
/// </summary>
public class DetailProjectDto
{
/// <summary>
/// 主键
/// </summary>
public string Id { get; set; }
/// <summary>
/// 工程名称
/// </summary>
[StringLength(250)]
[Required]
[Export("工程名称")]
public string ProjectName { get; set; }
/// <summary> /// 工程编号
/// </summary>
[StringLength(250)]
[Required]
[Export("工程编号")]
public string ProjectNo { get; set; } }
}
test.cshtml
@using Microsoft.AspNetCore.Html;
@using RunGo.ProjectsManager;
<h2 style="text-align:center;font-family: STSong;">工程基本信息</h2>
<table style="table-layout: fixed;word-break: break-all;border: 1px solid #000000;border-collapse: collapse;">
<tr style="height: 60px;font-size: 12px;">
<td style="border: 1px solid #000000;border-collapse: collapse;font-family: STSong;text-align: center; font-size:14px;font-weight:600;" colspan="2">工程名称</td>
<td style="border: 1px solid #000000;border-collapse: collapse;font-family: STSong;text-align: center;">
@Model.ProjectName
</td>
<td style="border: 1px solid #000000;border-collapse: collapse;font-family: STSong;text-align: center; font-size:14px;font-weight:600;" colspan="2">工程编号</td>
<td style="border: 1px solid #000000;border-collapse: collapse;font-family: STSong;text-align: center;">
@Model.ProjectNo
</td>
</tr>
</table>
接口
注:Nuget需要引入RazorEngine.NetCore
using RazorEngine; using RazorEngine.Templating;
/// <summary>
/// 工程基本信息报告下载
/// </summary>
/// <param name="projectId">工程id</param>
/// <returns></returns>
[HttpGet]
public FileResult UploadProjectBaseInfo(string projectId)
{
string memi = string.Empty;
Stream outData = null;
outData = GetBaseInfo(projectId, out memi);
return File(outData, memi, $"工程基本信息.docx");
}
/// <summary>
/// 工程基本信息
/// </summary>
/// <param name="projectId"></param>
/// <param name="memi"></param>
/// <returns></returns>
[RemoteService(false)]
public Stream GetBaseInfo(string projectId, out string memi)
{
var model = _projectManagerService.Detail(projectId).Result;
if (model == null)
{
throw new Exception("");
}
var template = System.IO.File.ReadAllText($"{_hostingEnvironment.WebRootPath}\\test.cshtml");
var html = Engine.Razor.RunCompile(template, Guid.NewGuid().ToString(), typeof(DetailProjectDto), model);
var op = _SpireDocHelper.SwaggerHtmlConvers(html, ".docx", out memi);
if (op == null)
throw new Exception("转换失败");
return op;
}
相关工具方法:
注:Nuget需要引入 Spire.Doc
public Stream SwaggerHtmlConvers(string html, string type, out string memi)
{
string fileName = Guid.NewGuid().ToString() + type;
string webRootPath = _hostingEnvironment.WebRootPath;
string path = webRootPath + @"\Files\TempFiles\";
var addrUrl = path + $"{fileName}";
FileStream fileStream = null;
var provider = new FileExtensionContentTypeProvider();
memi = provider.Mappings[type];
try
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
var data = System.Text.Encoding.Default.GetBytes(html);
var stream = new MemoryStream(data);
//创建Document实例
Document document = new Document();
//加载HTML文档
document.LoadFromStream(stream, FileFormat.Html, XHTMLValidationType.None);
document.SaveToFile(addrUrl, FileFormat.Docx);
document.Close();
fileStream = System.IO.File.Open(addrUrl, FileMode.OpenOrCreate);
var filedata = ByteHelper.StreamToBytes(fileStream);
var outdata = ByteHelper.BytesToStream(filedata);
return outdata;
}
catch (Exception e)
{
return null;
}
finally
{
if (fileStream != null)
fileStream.Close();
if (System.IO.File.Exists(addrUrl))
System.IO.File.Delete(addrUrl);//删掉文件
}
}
public static byte[] StreamToBytes(Stream stream)
{
byte[] bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
// 设置当前流的位置为流的开始
stream.Seek(0, SeekOrigin.Begin);
return bytes;
}
/// 将 byte[] 转成 Stream
public static Stream BytesToStream(byte[] bytes)
{
Stream stream = new MemoryStream(bytes);
return stream;
}
这样可以有效利用前端开发编写页面的速度远大于操作word书签的速度,使得后端开发只需针对完成后的页面进行实体绑定即可生成Word,大大提高了开发效率,接口成型之后,只需要提供cshtml文件以及实体即可实现绑定;
甚至可以将对应的cshtml路径以及对应的实体写入配置文件,通过反射来控制接口生成的Word,实现热更新。