asp.net mvc 模板生成word文档

自从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模板.需要动态生成的部分可以完全在里面实现.不需要任何多余的东西.再添加一个模板将会是一件很轻松的事,而且重要的是你不再需要重新生成你的工程项目.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值