asp.net mvc 模板生成word文档

来源:互联网 发布:mac系统清理工具 免费 编辑:程序博客网 时间:2024/05/16 05:30

自从ms office 2007开始.就开始支持open xml.而且发布了一套api.还有个对应的 open xml tool,我看了一下,觉得不值得花这个力气去熟悉一套非标准的api,在网上查资料的时候,我知道了原来docx的原来就是一个标准zip包,试着解压来看了看,文件结构比较简单,如果忽略掉文件之样的关联引用.完成可以自己生成xml,然后再打包成docx.为什么不呢,这样应该相当的简单.当然中间我也走了一点弯路,比如可恶的zippackage.好了.让我们开始吧.

step 1:当然是我们的controller.

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mvc;using CJRApp2.Data;using System.IO;using System.Text;using ICSharpCode.SharpZipLib.Zip;using ICSharpCode.SharpZipLib.Checksums;namespace CJRApp2.Web.Controllers{    public class CreatedocController : Controller    {                /// <summary>        /// uri调用格式可能为Createdoc?templateName=simple&arg=1&key=value        /// </summary>        /// <param name="templateName"></param>        /// <returns></returns>        public ActionResult Index(string templateName)        {            //WebFormView            //我们的约定是模板应该生成两个ViewData            //入口为模板名,参数设置应该模板文件自己去分析uri            //docx用于存放文件内容            //docName用于存放下载后的文件名            this.transView("~/Views/Createdoc/" + templateName + ".aspx");            byte[] bytes = this.ViewData["docx"] as byte[];            return this.File(bytes                , "application/vnd.openxmlformats-officedocument.wordprocessingml.document"                , this.ViewData["docName"] + ".docx");        }        /// <summary>        /// 由视图产生输出字符串,docx中的xml默认是utf-8无签名的.如果改用.aspx视图动态输出记得另存为有签名的        /// </summary>        /// <param name="viewName">视图文件名</param>        /// <returns>视图结果</returns>        private string transView(string viewName)        {            ViewEngineResult ver = ViewEngines.Engines.FindView(this.ControllerContext, viewName, null);            StringWriter sw = new StringWriter();            ViewContext vc = new ViewContext(this.ControllerContext, ver.View, this.ViewData, this.TempData, sw);            ver.View.Render(vc, sw);            ver.ViewEngine.ReleaseView(this.ControllerContext, ver.View);            sw.Flush();            string returnValue = sw.GetStringBuilder().ToString();            sw.Close();            sw.Dispose();            return returnValue;        }                /// <summary>        /// 转换视图结果进压缩包        /// </summary>        /// <param name="zs">压缩文件输出流</param>        /// <param name="uri">压缩文件uri</param>        /// <param name="viewName">视图文件名,形如~/Views/ctrollername/templatename/word/document.aspx</param>        /// <returns></returns>        public void transView2zip(ZipOutputStream zs, string uri, string viewName)        {            string fileContent=this.transView(viewName);            ZipEntry entry = new ZipEntry(uri);            zs.PutNextEntry(entry);            byte[] bytes = Encoding.UTF8.GetBytes(fileContent);            zs.Write(bytes, 0, bytes.Length);                    }        /// <summary>        /// 添加源文件到压缩包        /// </summary>        /// <param name="zs">压缩文件输出流</param>        /// <param name="uri">压缩文件uri</param>        /// <param name="file">本地文件全</param>        public void AddFile2zip(ZipOutputStream zs,string uri,string file) {            FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read);            ZipEntry entry = new ZipEntry(uri);            zs.PutNextEntry(entry);            fs.CopyTo(zs);            fs.Flush();            fs.Close();            fs.Dispose();        }                 }}
step 2:现在我们希望有一个模板文件template.aspx提供zip包存放在ViewData["docx"]中就可以提供服务了,它应该就是一个根据需要生成zip包的过程,我们先创建一个simple.aspx的视图文件,建立一个simple的对应目录存放解压后的docx,可能的结构如图所示


现在开始我们的simple.aspx吧.

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %><%@ import namespace="CJRApp2.Web.Controllers" %><%@ import namespace="System.IO" %><%@ import namespace="ICSharpCode.SharpZipLib.Zip" %><%    //文件名应该为~/......xxx.aspx?    CreatedocController ctrl = ViewContext.Controller as CreatedocController;    HttpContextBase http = ViewContext.HttpContext;        string viewpath = (this.ViewContext.View as WebFormView).ViewPath;    string filePath = viewpath.Substring(0, viewpath.Length - 5) + "\\";    filePath = Server.MapPath(filePath);   // String[] files= Directory.GetFileSystemEntries(filePath);    IEnumerable<string> files= Directory.EnumerateFiles(filePath, "*", SearchOption.AllDirectories);    using (MemoryStream ms = new MemoryStream())    {        //不要考虑ZipPackage,微软自己都说它不是一个标准的zip,试试SharpZip        using (ZipOutputStream zs = new ZipOutputStream(ms))        {            zs.UseZip64 = UseZip64.Off;            //非aspx直接进压缩包            foreach (string file in files)            {                string uri = file.Substring(filePath.Length);                if (!uri.EndsWith(".aspx")){                    ctrl.AddFile2zip(zs, uri, file);                }            }            //现在我们修改了word/document.xml=>word/document.aspx            //事实上可以用同一个aspx生成不同的xml然后进压缩包,比如类似的chart            //实现在里面document.aspx            ctrl.transView2zip(zs, "word/document.xml",               "~/views/createdoc/simple/word/document.aspx");             }                //设置ctrl.ViewData 以返回数据,现在所有的生成过程都是在viewpage中完成的        //至于controller 会读取 ViewData,然后以docName指定的名字返回压缩文件,也就是docx        ctrl.ViewData["docx"] = ms.ToArray();        ctrl.ViewData["docName"] = "下载后的文件名为simple";    }%>
step3:很好,它开始工作了.但这不是我们所需要的.我们的工作应该是document.aspx,因为我们需要它来动态的生成word/document.xml,尝试重命word\documents.xml为
aspx,simple能根据我们定义的规则输出视图并添加入zip包,这很轻松,不是吗,当然有点小小的友情提供.docx解压出来的xml文件是utf-8(无签名)格式的,需要另存为有签名的,为了方便我们修改document.aspx.我们可能需要格式化xml.记得不要在xml文档声明之前添加回车.换行.可能会导致文档的开始部分有点点丑.它的第一行可能是这样的

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %><?xml version="1.0" encoding="UTF-8" standalone="yes"?>

我们可以试着加入我们的逻辑在这个document.aspx中.比如做为示例.我添加了一点点内容

 

 <%foreach (string key in Request.QueryString.AllKeys) { %>    <w:p w:rsidR="003047AC" w:rsidRDefault="003047AC">      <w:pPr>        <w:rPr>          <w:rFonts w:hint="eastAsia"/>        </w:rPr>      </w:pPr>      <w:r>        <w:rPr>          <w:rFonts w:hint="eastAsia"/>        </w:rPr>        <w:t><%=key + ":" + Request.QueryString[key]%></w:t>      </w:r>    </w:p>    <%} %>

step 4:收工.总结一下,我们需要提供的是一个aspx文件和一个对应的docx解压目录.aspx用于指导怎么生成压缩包.压缩包里的文档就是我们的docx模板.需要动态生成的部分可以完全在里面实现.不需要任何多余的东西.再添加一个模板将会是一件很轻松的事,而且重要的是你不再需要重新生成你的工程项目.

原创粉丝点击