精华内容
下载资源
问答
  • 根据模板生成word文档下载

    千次阅读 2017-07-04 10:36:38
    需求:根据数据库已有字段,填入写好的word并下载 工具 :freemark 一种方便生成word的引擎,内置好大量基础方法 思路: 一.将数据库数据按需求(根据id,根据name......)提取存储在一个map中待用 二.将模板word生成ftl文件...

    需求:根据数据库已有字段,填入写好的word并下载

    工具 :freemark   一种方便生成word的引擎,内置好大量基础方法

    思路:

    一.将数据库数据按需求(根据id,根据name......)提取存储在一个map中待用

    二.将模板word生成ftl文件,以便freemark来改造

    三.将存有数据的map依靠WordUtils工具类传递数据给ftl

    四.前台连接,生成word

    详解:

    步骤一

    1.按照需求,根据id查找一个po实例,使用到sql语句查询

    mapping:

    <select id="findById" parameterType="String"
    resultType="com.kanq.ly.pwf.model.PwfContract">
    select t.* from PWF_CONTRACT t where t.id = #{id}
    </select>

    ctrl:

     private String downloadWord(HttpServletRequest request,HttpServletResponse response,String id) throws IOException{
            //根据id获取一个实例
            System.out.println("合同id:"+id);
            PwfContract pwfContract=getEntryById(id);
           
           
            //map中存放需要生成word文档的合同具体信息
            Map<String, Object> mapValue = new HashMap<String, Object>();
                mapValue.put("cdate", pwfContract.getCdate());
                mapValue.put("category", pwfContract.getCategory());
               
               
                //利用工具类生成文档,需要修改工具类的具体信息
                WordUtils.exportMillCertificateWord(request,response,mapValue);
            return null;
            }

    步骤二:

    1.把word模板中需要填充的数据进行修改  ,把   a  改成  ${a}  

    注意!!!此时存在一个问题:当a为空的时候,后台会报一个null的错误,freemark无法自动填充一个null值,需要进行判断,把  a改成  <#if a??> ${(a)!""}</#if>意思为如果a不存在 a的值默认为"",!是默认的意思,

    所以最后把a修改成<#if a??> ${(a)!""}</#if>,然后保存文件类型为xml

    2.把xml的文件打开,会观察到  <#if a??> ${(a)!""} 这句话会被填充一些字符(这些字符是word文档一些修饰的东西,比如说字体大小颜色粗细之类的),需要把这些填充的字符删除,只留下纯粹的<#if a??> ${(a)!""}这句话,然后保存为ftl的文件

    注意!!!freemark这个引擎填充数据的原理是通过对照字段,比如在map中存放的字段为  name  在ftl中就会找name的字段填充进去,如果对应不上,就无法填充,无字段前后顺序要求

    步骤三;

    1.首先需要一个WordUtils工具类,百度上一大堆,用来查找ftl文件位置以及整合map

    package com.kanq.framework.utils.tool;
    
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.net.URLEncoder;
    import java.util.Map;
    
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    
    import freemarker.template.Configuration;
    import freemarker.template.Template;
    
    
    public class WordUtils {
    	 //配置信息,代码本身写的还是很可读的,就不过多注解了  
        private static Configuration configuration = null;  
        //这里注意的是利用WordUtils的类加载器动态获得模板文件的位置  ,修改ftl文件的位置                                     类加载器获取数据源 路径              获取路径     
        private static final String templateFolder = WordUtils.class.getClassLoader().getResource("../../").getPath() + "pages/wordTemplate";  
        
        static {  
            configuration = new Configuration();  
            configuration.setDefaultEncoding("utf-8");  
            try {  
            	//引擎搜索ftl文件的基础路径设置
                configuration.setDirectoryForTemplateLoading(new File(templateFolder));  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
       }  
      
        private WordUtils() {  
            throw new AssertionError();  
        }  
      
        public static void exportMillCertificateWord(HttpServletRequest request, HttpServletResponse response, Map map) throws IOException {  
        	//在默认路径上找到相应的ftl文件
        	Template freemarkerTemplate = configuration.getTemplate("pwf.ftl");  
            File file = null;  
            InputStream fin = null;  
            ServletOutputStream out = null;  
            try {  
                // 调用工具类的createDoc方法生成Word文档  
                file = createDoc(map,freemarkerTemplate);  
                fin = new FileInputStream(file);  
                //设置响应类型是文档
                response.setCharacterEncoding("utf-8");  
                response.setContentType("application/msword");  
                // 设置浏览器以下载的方式处理该文件名  
                String fileName = "管护合同.doc";  
                response.setHeader("Content-Disposition", "attachment;filename="  
                        .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));  
      
                out = response.getOutputStream();  
                byte[] buffer = new byte[512];  // 缓冲区  
                int bytesToRead = -1;  
                // 通过循环将读入的Word文件的内容输出到浏览器中  
                while((bytesToRead = fin.read(buffer)) != -1) {  
                    out.write(buffer, 0, bytesToRead);  
                }  
            } finally {  
                if(fin != null) fin.close();  
                if(out != null) out.close();  
                if(file != null) file.delete(); // 删除临时文件  
            }  
        }  
      
        private static File createDoc(Map<?, ?> dataMap, Template template) {  
            String name =  "管护合同.doc";  
            File f = new File(name);  
            Template t = template;  
            try {  
                // 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开  
                Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");  
                t.process(dataMap, w);  
                w.close();  
            } catch (Exception ex) {  
                ex.printStackTrace();  
                throw new RuntimeException(ex);  
            }  
            return f;  
        }  
    }  
    
    
    
    
    
    
    

    该工具类的主要方法为:createDoc(map元数据,ftl文件){}  根据map源数据生成doc文件 

    exportMillCertificateWord(请求,响应,map元数据){} 配置生成doc的环境


    2.在ctrl中利用WordUtils中的exportMillCertificateWord生成一个Word

    WordUtils.exportMillCertificateWord(request,response,mapValue);

    步骤四:

    1.html中添加超链传递id

    <a href="javascript:void(0)" οnclick="pwfContract.exportWord('{{id}}')">生成合同</a>

    2.js中接收id传递给后台

     var exportWord = function(id){
    window.open("../pwf/pwfContract/downloadWord.do?id="+id);}

    3.后台接收id,作为查询po实例关键字

     @RequestMapping(value="downloadWord.do")
            private String downloadWord(HttpServletRequest request,HttpServletResponse response,String id) throws IOException{
            //根据id获取一个实例
            System.out.println("合同id:"+id);
            PwfContract pwfContract=getEntryById(id)
    ;
    }

    展开全文
  • 在前端如何玩转 Word 文档

    千次阅读 2020-07-20 17:14:31
    在日常工作中,大部分人都会使用 Microsoft Office Word、WPS 或 macOS Pages 等文字处理程序进行 Word 文档处理。除了使用上述的文字处理程序之外,对...

    在日常工作中,大部分人都会使用 Microsoft Office Word、WPS 或 macOS Pages 等文字处理程序进行 Word 文档处理。除了使用上述的文字处理程序之外,对于 Word 文档来说,还有其他的处理方式么?答案是有的。

    接下来阿宝哥将介绍在前端如何玩转 Word 文档,阅读本文之后,你将了解以下内容:

    • Microsoft Office Word 支持的文件格式和 Docx 文档的特点;

    • 如何将 Word 文档转换成 HTML 文档;

    • 如何在浏览器中处理 ZIP 文档;

    • 如何将 Word 文档转换成 Markdown 文档;

    • 如何在前端动态生成 Word 文档。

    小伙伴们准备好了吗,「玩转 Word 文档之旅」 开始了,Let's go!

    一、Microsoft Office Word 简介

    Microsoft Office Word 是微软公司的一个文字处理器应用程序。它最初是由 Richard Brodie 为了运行 DOS 的 IBM 计算机而在 1983 年编写的。随后的版本可运行于 Apple Macintosh(1984 年)、SCO UNIX 和 Microsoft Windows(1989 年),并成为了 Microsoft Office 的一部分。

    Word 给用户提供了用于创建专业而优雅的文档工具,帮助用户节省时间,并得到优雅美观的结果。一直以来,Microsoft Office Word 都是最流行的文字处理程序。

    1.1 Word 支持的文件格式

    下表列出了常见的几种 Word 支持的文件格式,按扩展名的字母顺序排序。

    若想了解 Word 所有支持的格式,可参考微软 office-file-format-reference 在线文档。目前大家接触比较多的是扩展名为 .docx 的文档,因此它就是本文的主角。

    1.2 Docx 文档

    俗话说 “知己知彼百战百胜”,在 “出战” 前我们先来简单了解一下 「docx」 文档。「97-2003 的旧版本文件名后缀就是 .doc, 2007 版以后的后缀名是 .docx」。docx 格式是被压缩过的文档,体积更小,能处理更加复杂的内容,访问速度更快。

    实际上 「docx」 文档是一个压缩文件( ZIP 格式)。ZIP 文件格式是一种数据压缩和文档储存的文件格式,原名 Deflate,发明者为菲尔·卡茨(Phil Katz),他于 1989 年 1 月公布了该格式的资料。ZIP 通常使用后缀名 “.zip”,它的 MIME 格式为 「application/zip」

    这里阿宝哥已经提前准备了一个包含阿宝哥头像和某些文本的 「abao.docx」 文档,接着复制一份重命名为 「abao.zip」,然后使用 ZIP 压缩/解压软件进行解压。

    通过观察解压后的目录,我们发现 Word 文档由一系列的 XML 文件和多媒体文件组成, 「abao.docx」 文档中的阿宝哥头像,最终被解压到 「word/media」 目录下。下面我们来查看一下 abao 文件夹的目录结构:

    -rw-rw-r--@  1 fer  staff  1641  7 11 01:25 [Content_Types].xml
    drwxr-xr-x@  3 fer  staff    96  7 11 09:41 _rels
    drwxr-xr-x@  4 fer  staff   128  7 11 09:41 docProps
    drwxr-xr-x@ 13 fer  staff   416  7 11 09:42 word
    

    很明显 abao 目录下含有一个 「[Content_Types].xml」 文件和 「_rels、docProps、word」 三个子目录。

    • [Content_Types].xml:该文件用于定义里面每一个 XML 文件的内容类型;

    • _rels:该目录下一般会有一个 「.rels」 后缀的文件,它里面保存了这个目录下各个 Part 之间的关系。_rels 目录不止一个,它实际上是有层级的。

    • docProps:该目录下的 XML 文件用于保存 docx 文件的属性;

    • word:该目录下包含了 Word 文档中的内容、字体、样式或主题等信息。

    介绍完 Word 支持的文件格式和 Docx 文档,我们开始进入正题 —— 「“在前端如何玩转 Word 文档”」

    二、Word 文档转换成 HTML 文档

    在日常工作中,有些时候我们希望在富文本编辑器中导入已有的 Word 文档进行二次加工,要满足这个需求,我们就需要先把 Word 文档转换成 HTML 文档。要实现这个功能,有 「服务端转换和前端转换」 两种方案:

    • 服务端转换:对于 Java 开发者来说,可以直接基于 POI 项目,POI 是 Apache 的一个开源项目,它的初衷是处理基于 Office Open XML 标准(OOXML)和 Microsoft OLE 2 复合文档格式(OLE2)的各种文件格式的文档,而且支持读写操作。

    • 前端转换:对于前端开发者来说,要想在前端解析 Word 文档,我们首先需要对 Word 文档进行解压,然后再进一步解析解压后的 XML 文档。看起来整个功能实现起来比较繁琐,但值得庆幸的是 Mammoth.js 这个库已经为我们实现上述功能。

    在介绍如何利用 Mammoth.js 把之前创建的 Word 文档转换成 HTML 文档前,我们来提前体验一下最终的转换效果。

    2.1 Mammoth.js 简介

    Mammoth.js 旨在转换 .docx 文档(例如由 Microsoft Word 创建的文档),并将其转换为 HTML。「Mammoth 的目标是通过使用文档中的语义信息并忽略其他细节来生成简单干净的 HTML。」 比如,Mammoth 会将应用标题 1 样式的任何段落转换为 h1 元素,而不是尝试完全复制标题的样式(字体,文本大小,颜色等)。

    由于 .docx 使用的结构与 HTML 的结构之间存在很大的不匹配,这意味着对于较复杂的文档而言,这种转换不太可能是完美的。但如果你仅使用样式在语义上标记文档,则 Mammoth 能实现较好的转换效果。

    当前 Mammoth 支持以下主要特性:

    • Headings

    • Lists,Table

    • Images

    • Bold, italics, underlines, strikethrough, superscript and subscript

    • Links,Line breaks

    • Footnotes and endnotes

    它还支持自定义映射规则。例如,你可以通过提供适当的样式映射将 WarningHeading 转换为 h1.warning。另外文本框的内容被视为单独的段落,出现在包含文本框的段落之后。

    Mammoth.js 这个库为我们提供了很多方法,这里我们来介绍三个比较常用的 API:

    • mammoth.convertToHtml(input, options):把源文档转换为 HTML 文档

    • mammoth.convertToMarkdown(input, options):把源文档转换为 Markdown 文档。这个方法与 convertToHtml 方法类似,区别就是返回的 result 对象的 value 属性是 Markdown 而不是 HTML。

    • mammoth.extractRawText(input):提取文档的原始文本。这将忽略文档中的所有格式。每个段落后跟两个换行符。

    介绍完 Mammoth.js 相关的特性和 API,接下来我们开始进入实战环节。

    2.2 Mammoth.js 实战

    Mammoth.js 这个库同时支持 Node.js 和浏览器两个平台,在浏览器端 mammoth.convertToHtml 方法的 input 参数的格式是 {arrayBuffer: arrayBuffer},其中 arrayBuffer 就是 .docx 文件的内容。在前端我们可以通过 FileReader API  来读取文件的内容,此外该接口也提供了 readAsArrayBuffer 方法,用于读取指定的 Blob 中的内容,一旦读取完成,result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象。下面我们定义一个 readFileInputEventAsArrayBuffer 方法:

    export function readFileInputEventAsArrayBuffer(event, callback) {
      const file = event.target.files[0];
    
      const reader = new FileReader();
    
      reader.onload = function(loadEvent: Event) {
        const arrayBuffer = loadEvent.target["result"];
        callback(arrayBuffer);
      };
    
      reader.readAsArrayBuffer(file);
    }
    

    该方法用于实现把输入的 File 对象转换为 ArrayBuffer 对象。在获取 Word 文档对应的 ArrayBuffer 对象之后,就可以调用 convertToHtml 方法,把 Word 文档内容转换为 HTML 文档。

    mammoth.convertToHtml({ arrayBuffer })
    

    此时如果你的文档中不包括特殊的图片类型,比如 wmfemf 类型,而是常见的 jpgpng 等类型的话,那么你可以看到 Word 文档中的图片。难道这样就搞定了,那是不是太简单了,其实这只是个开始。当你通过浏览器的开发者工具审查 Word 解析后的 HTML 文档后,会发现图片都以 Base64 的格式进行嵌入。如果图片不多且单张图片也不会太大的话,那这种方案是可以考虑的。

    针对多图或大图的情况,一种比较好的方案是把图片提交到文件资源服务器上。在 Mammoth.js 中要实现上述的功能,可以使用 「convertImage」 配置选项来自定义图片处理器。具体的使用示例如下:

    let options = {
        convertImage: mammoth.images.imgElement(function(image) {
          return image.read("base64").then(function(imageBuffer) {
            return {
              src: "data:" + image.contentType + ";base64," + imageBuffer
            };
          });
        })
    };
    

    以上示例实现的功能就是把 Word 中的图片进行 Base64 编码,然后转成 Data URL 的形式,以实现图片的显示。很明显这不符合我们的要求,所以我们需要做以下调整:

    const mammothOptions = {
      convertImage: mammoth.images.imgElement(function(image) {
        return image.read("base64").then(async (imageBuffer) => {
          const result = await uploadBase64Image(imageBuffer, image.contentType);
          return {
            src: result.data.path // 获取图片线上的URL地址
          };
        });
      })
    };
    

    顾名思义 uploadBase64Image 方法的作用就是上传 Base64 编码后的图片:

    async function uploadBase64Image(base64Image, mime) {
      const formData = new FormData();
      formData.append("file", base64ToBlob(base64Image, mime));
      
      return await axios({
        method: "post",
        url: "http://localhost:3000/uploadfile", // 本地图片上传的API地址
        data: formData,
        config: { headers: { "Content-Type": "multipart/form-data" } }
      });
    }
    

    为了减少图片文件的大小,我们需要把 Base64 格式的图片先转成 Blob 对象,然后在通过创建 FormData 对象进行提交。base64ToBlob 方法的定义如下:

    function base64ToBlob(base64, mimeType) {
      let bytes = window.atob(base64);
      let ab = new ArrayBuffer(bytes.length);
      let ia = new Uint8Array(ab);
      for (let i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i);
      }
      return new Blob([ia], { type: mimeType });
    }
    

    这时把 Word 文档转换为 HTML 并自动把 Word 文档中的图片上传至文件资源服务器的基本功能已经实现了。对于 Mammoth.js 内部是如何解析 Word 中的 XML 文件,我们就不做介绍了,反之我们来简单介绍一下 Mammoth.js 内部依赖的 JSZip 这个库。

    2.3 JSZip 简介

    JSZip 是一个用于创建、读取和编辑 「.zip」 文件的 JavaScript 库,含有可爱而简单的 API。该库的兼容性如下所示:

    OperaFirefoxSafariChromeInternet ExplorerNode.js
    YesYesYesYesYesYes
    经过最新版本的测试经过 3.0/3.6/最新版本测试经过最新版本的测试经过最新版本的测试经过 IE 6 / 7 / 8 / 9 / 10 测试经过 Node.js 0.10 / 最新版本测试

    2.3.1 JSZip 安装

    使用 JSZip 时,你可以通过以下几种方式进行安装:

    • 「npm」npm install jszip

    • 「bower」bower install Stuk/jszip

    • 「component」component install Stuk/jszip

    • 「手动」:先下载  JSZip 安装包,然后引入 dist/jszip.js 或  dist/jszip.min.js 文件

    2.3.2 JSZip 使用示例
    let zip = new JSZip();
    zip.file("Hello.txt", "Hello Semlinker\n");
    
    let img = zip.folder("images");
    img.file("smile.gif", imgData, {base64: true});
    zip.generateAsync({type: "blob"})
    .then(function(content) {
        // see FileSaver.js
        saveAs(content, "example.zip");
    });
    

    该示例来自 JSZip 官网,成功运行之后,会自动下载并保存 「example.zip」 文件。该文件解压后的目录结构如下所示:

    三、Word 文档转换成 Markdown 文档

    「Markdown 是一种轻量级标记语言」 ,创始人为约翰·格鲁伯(英语:John Gruber)。它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的 XHTML(或者 HTML)文档。这种语言吸收了很多在电子邮件中已有的纯文本标记的特性。

    由于 Markdown 的轻量化、易读易写特性,并且对于图片,图表、数学式都有支持,目前许多网站都广泛使用 Markdown 来撰写帮助文档或是用于论坛上发表消息。

    了解完 Markdown 是什么之后,我们来分析一下如何把 Word 文档转换成 Markdown 文档。对于这个功能,我们也有两种处理方式:

    • 第一种:使用 Mammoth.js 这个库提供的 mammoth.convertToMarkdown(input, options) 方法;

    • 第二种:基于 mammoth.convertToHtml(input, options) 生成的 HTML 文档,在利用 HTML to Markdown 的转换工具,来间接实现上述功能。

    下面我们来介绍第二种方案,这里我们使用 Github 上一个开源的转换器 ——  turndown,它是使用 JavaScript 开发的 HTML to Markdown 转换器,使用起来很简单。

    首先你可以通过以下两种方式来安装它:

    • npm:npm install turndown

    • script:<script src="https://unpkg.com/turndown/dist/turndown.js"></script>

    安装完之后,你就可以通过调用 TurndownService 构造函数,来创建 turndownService 实例,然后调用该实例的 turndown() 方法执行转换操作:

    let markdown = turndownService.turndown(
      document.getElementById('content')
    )
    

    对于前面使用的 「abao.docx」 文档,最终转换生成的 Markdown 文档如下:

    全栈修仙之路,聚焦全栈,专注分享 TypeScript、Web API、Node.js、Deno 等全栈干货。
    
    ![](https://cdn.xxx.com/rich_159444942843202)
    

    需要注意的是,TurndownService 构造函数支持很多配置项,这里阿宝哥就不详细介绍了。感兴趣的小伙伴,可以自行阅读 turndown 官方文档或访问 turndown 在线示例 实际体验一下。

    既然已经讲到 Markdown,阿宝哥再给小伙伴们介绍一个 Github 上不错的开源库 markmap,该库使用思维导图的方式来实现 Markdown 文档的可视化,整体效果还蛮不错的:

    (图片来源:https://markmap.js.org/repl/)

    最后,我们再来看一下在前端如何动态生成 Word 文档。

    四、前端动态生成 Word 文档

    在前端如果要动态生成 Word 文档,我们可以直接利用一些成熟的第三方开源库,比如:docx 或 html-docx-js。

    下面我们将以 docx 为例,来介绍如何在前端如何生成 「.docx」 格式的 Word 文档。Docx 这个库提供了优雅的声明式 API,让我们可以使用 JS/TS 轻松生成 .docx 文件。此外,它还同时支持 Node.js 和浏览器。

    Docx 这个库为开发者提供了许多类,用于创建 Word 中的对应元素,这里我们简单介绍几个常见的类:

    • Document:用于创建新的 Word 文档;

    • Paragraph:用于创建新的段落;

    • TextRun:用于创建文本,支持设置加粗、斜体和下划线样式;

    • Tables:用于创建表格,支持设置表格每一行和每个表格单元的内容。

    接下来阿宝哥将使用 Docx 这个库,来动态生成前面介绍过的 「abao.docx」 文档,具体代码如下所示:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title></title>
      </head>
      <body>
        <h1>阿宝哥 - 动态生成 Word 文档示例</h1>
    
        <button type="button" onclick="generate()">
          点击生成 Docx 文档
        </button>
        <script src="https://unpkg.com/docx@5.0.2/build/index.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.js"></script>
        <script>
          async function generate() {
            const doc = new docx.Document();
    
            const imageBuffer = await fetch(
              "https://avatars3.githubusercontent.com/u/4220799"
            ).then((response) => response.arrayBuffer());
    
            const image = docx.Media.addImage(doc, imageBuffer, 230, 230);
    
            doc.addSection({
              properties: {},
              children: [
                new docx.Paragraph({
                  children: [
                    new docx.TextRun({
                      text: "全栈修仙之路,",
                      bold: true,
                    }),
                    new docx.TextRun({
                      text:
                        "聚焦全栈,专注分享 TypeScript、Web API、Node.js、Deno 等全栈干货。",
                    }),
                  ],
                }),
                new docx.Paragraph(image),
              ],
            });
    
            docx.Packer.toBlob(doc).then((blob) => {
              console.log(blob);
              saveAs(blob, "abao.docx");
              console.log("文档生成成功");
            });
          }
        </script>
      </body>
    </html>
    

    在以上示例中,当用户点击 「点击生成 Docx 文档」 按钮之后,会调用 generate() 回调函数。在该回调函数内,首先会创建新的 Document 对象,然后使用 fetch API 从 Github 上下载阿宝哥的头像,当成功获取图片的数据之后,会继续调用 docx.Media.addImage() 方法添加图片。

    接着我们会调用 doc.addSection() 方法来添加 Section 块,该块将作为段落的容器。在示例中,我们创建的 Section 块包含两个段落,一个用于存放文本信息,而另一个用于存放图片信息。最后我们会把 Document 对象转换成 Blob 对象,然后通过 saveAs() 方法下载到本地。

    五、参考资源

    • MDN - FileReader

    • 百度百科 - Microsoft Office Word

    • office-file-format-reference

    • Github - mammoth.js

    关于奇舞周刊

    《奇舞周刊》是360公司专业前端团队「奇舞团」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。

    展开全文
  • 公司前一段时间做的一个项目中应用到了这个技术,需要后台获取数据后在前台直接下载word文档,利用freemarker模版生成的doc文档在电脑上可以正常打开,但是发送到手机上打开则全部变成“乱码”。但实际上并不是乱码...

       公司前一段时间做的一个项目中应用到了这个技术,需要后台获取数据后在前台直接下载word文档,利用freemarker模版生成的doc文档在电脑上可以正常打开,但是发送到手机上打开则全部变成“乱码”。但实际上并不是乱码,而是xml格式的代码,在手机等移动端显示不出正常的文档信息。之后辗转查询使用了很多方案,比如利用poi操作,利用jacob进行格式转换,但是效果都不尽如人意,而且处理过程非常复杂,浪费了大量时间。最终,通过不断摸索,找到了非常完美的解决方案。不需要先生成xml格式的doc文档再去转换,而是戒指在后台利用模版添加数据后,利用IO流进行替换,可以直接生成标准的docx文档。

        首先需要理解的一点是,docx文档本身是一个压缩文件。在利用这个方案前要先准备好docx模版。在这个docx模版中,将要替换的数据用${}的方式替换掉,先占好位置。如图:

        将docx改后缀名为.zip并解压出来。在word文件夹下有一个文件为、:document.xml

        利用notpad等工具打开此文件,将格式修改好,比如在xml文件中${xxx}分别氛围${ ,  xxx 以及 } 三部分存贮在不同的标签下,要将他们合并到一起。

        随后将保存好的document.xml和之前定义好的zip文件放入工程的配置文件夹下,方便引用。

        核心代码如下

      Configuration
                 configuration = new Configuration();
         /** 设置编码 **/
         /** 我的ftl文件是放在D盘的**/
         String fileDirectory = "D:/cache/qqChace/T1/xlsx";
         /** 加载文件 **/
         configuration.setDirectoryForTemplateLoading(new File(fileDirectory));
         /** 加载模板 **/
         Template template = configuration.getTemplate("document.xml");
         /** 准备数据 **/
         Map<String,String> dataMap = new HashMap<>();
         /** 在ftl文件中有${textDeal}这个标签**/
    
         dataMap.put("id","哈士奇");
         dataMap.put("number","20");
         dataMap.put("language","java,php,python,c++.......");
         dataMap.put("example","Hello World!");
    
         /** 指定输出word文件的路径 **/
         String outFilePath = "D:/cache/qqChace/T1/xlsx/data.xml";
         File docFile = new File(outFilePath);
         FileOutputStream fos = new FileOutputStream(docFile);
         OutputStreamWriter oWriter = new OutputStreamWriter(fos);
         Writer out = new BufferedWriter(new OutputStreamWriter(fos),10240);
         template.process(dataMap,out);
    
         if(out != null){
             out.close();
         }
         // ZipUtils 是一个工具类,主要用来替换具体可以看github工程
         ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(new FileInputStream(new File("D:/cache/qqChace/T1/xlsx/test.zip")));
            ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(new FileOutputStream(new File("D:/cache/qqChace/T1/xlsx/test.docx")));
            String itemname = "word/document.xml";
            ZipUtils.replaceItem(zipInputStream, zipOutputStream, itemname, new FileInputStream(new File("D:/cache/qqChace/T1/xlsx/data.xml")));
            System.out.println("success");

        这段代码是转来的,https://blog.csdn.net/u013076044/article/details/79236000

       想要源码可以去这个地址下载。

       上面主要解决的问题事文字的转换,下面要说的关于图片的转换问题。

        将zip文件解压后会发现,document.xml中没有你原本保存在docx中的图片,图片是在word下的media文件夹下。document.xml中或许只是引用。但是仍可以通过这种方式,以流的形式替换图片。只要保证图片的顺序不乱就可以了。

       下面是我在上面这段代码上进行的改动

        String zipPath =  this.getClass().getResource("/freemarkerDoc.zip").getPath();
    	        try {
    	            ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(new FileInputStream(new File(zipPath)));
    	            ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(new FileOutputStream(new File(basePath+patientInfo.getName()+"连续血氧检测报告("+reportInfo.getId()+").docx")));//这个是要生成文档的路径和名称
    	            String itemname = "word/document.xml";
    	            String itemname2 = "word/media/";
    	            String picturePath = PropertiesUtil.getBinaryRoorPath("picturePath")+File.separator+patientInfo.getId()+File.separator+reportInfo.getId()+File.separator;
    	            ZipUtils.replaceItem(zipInputStream, zipOutputStream, itemname, new FileInputStream(new File(url)), itemname2, picturePath);
    	            System.out.println("success");
    
    	        } catch (Exception e) {
    	            System.out.println(e.toString());
    	        }

    我的docx文档中工有四张图片,所以在zipUtil中要传入你要替换上去的图片路径。代码如下

     public static void replaceItem(ZipInputStream zipInputStream,
                                       ZipOutputStream zipOutputStream,
                                       String itemName,
                                       InputStream itemInputStream
                                       ,
                                       String itemName2,
                                       String picturePath
        ){
            //
            if(null == zipInputStream){return;}
            if(null == zipOutputStream){return;}
            if(null == itemName){return;}
            if(null == itemInputStream){return;}
            InputStream itemIS = null;
            //
            ZipEntry entryIn;
            try {
                while((entryIn = zipInputStream.getNextEntry())!=null)
                {
                    String entryName =  entryIn.getName();
                    ZipEntry entryOut = new ZipEntry(entryName);
                    // 只使用 name
                    zipOutputStream.putNextEntry(entryOut);
                    // 缓冲区
                    byte [] buf = new byte[8*1024];
                    int len;
                    //image1.png等是media目录下的图片名称。
                    String ima1 = itemName2+"image1.png";
                    String ima2 = itemName2+"image2.png";
                    String ima3 = itemName2+"image3.png";
                    String ima4 = itemName2+"image4.png";
                    
                    if(entryName.equals(itemName)){
                        // 使用替换流
                        while((len = (itemInputStream.read(buf))) > 0) {
                            zipOutputStream.write(buf, 0, len);
                        }
                    }else if(entryName.equals(ima1)){
                        // 使用替换流,将需要替换上去的图片路径以流的形式获取
                    	itemIS = new FileInputStream(new File(picturePath+"spo2PiePicture.png"));
                        while((len = (itemIS.read(buf))) > 0) {
                            zipOutputStream.write(buf, 0, len);
                        }
                    }else if(entryName.equals(ima2)){
                      // 使用替换流
                		itemIS = new FileInputStream(new File(picturePath+"prPiePicture.png"));
    	                while((len = (itemIS.read(buf))) > 0) {
    	                    zipOutputStream.write(buf, 0, len);
    	                }
                    }else if(entryName.equals(ima3)){
                    // 使用替换流
                    	itemIS = new FileInputStream(new File(picturePath+"spo2Picture.png"));
    	                while((len = (itemIS.read(buf))) > 0) {
    	                   zipOutputStream.write(buf, 0, len);
    	                }
                    }else if(entryName.equals(ima4)){
                        // 使用替换流
                    	itemIS = new FileInputStream(new File(picturePath+"prPicture.png"));
    	                while((len = (itemIS.read(buf))) > 0) {
    	                    zipOutputStream.write(buf, 0, len);
    	                }
                    }else {
                        // 输出普通Zip流
                        while((len = (zipInputStream.read(buf))) > 0) {
                            zipOutputStream.write(buf, 0, len);
                        }
                    }
                    // 关闭此 entry
                    zipOutputStream.closeEntry();
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                //e.printStackTrace();
                close(itemInputStream);
                close(zipInputStream);
                close(zipOutputStream);
                close(itemIS);
            }
        }

    转换完成后要讲所有的流全部关闭,就完成了。

    展开全文
  • 4、选择对应的数据库类型 5、按照下图的顺序填写相关信息,注意一定要选择编码,不然导出的文档中文很有可能会乱码。 6、打开DBExportDoc V1.0 For MySQL-m.doc文件,如果你的word文档安装了宏插件的话将会弹出下图...

    前言

    首先我想说这个功能已经有很多博客记录过了,但是我在参考了很多博客操作的时候由于一些细节上的东西还是花了不少的时间,就是因为这些细节在博客中并未记录。所以我想在这里把整个完整的过程包括一些细节都记录下来,给自己做个备忘也可以给其他的童鞋一些参考。

    相关工具下载地址:

    https://github.com/flypangzhi/DBExportDocTool/blob/master/%E5%AF%BC%E5%87%BA%E6%95%B0%E6%8D%AE%E8%A1%A8%E7%BB%93%E6%9E%84%E5%B7%A5%E5%85%B7%E5%90%88%E9%9B%86.zip

    具体操作流程

    1、安装驱动

    打开DBExportDoc V1.0 For MySQL 文件夹,根据你的电脑系统选择合适的驱动,双击打开
    在这里插入图片描述
    然后一直点下一步直到安装成功
    在这里插入图片描述

    2、设置数据库的相关信息

    依次打开 控制面板\所有控制面板项\管理工具,找到数据源
    在这里插入图片描述

    3、双击打开,选择添加

    在这里插入图片描述

    4、选择对应的数据库类型

    在这里插入图片描述

    5、按照下图的顺序填写相关信息,注意一定要选择编码,不然导出的文档中文很有可能会乱码。

    在这里插入图片描述

    6、打开DBExportDoc V1.0 For MySQL-m.doc文件,如果你的word文档安装了宏插件的话将会弹出下图窗口,然后选择启动宏。如果没弹出对话框的话说明没安装宏插件,解决方案就是安装你的word文档版本对应的插件,比如说vbaforwps2017,如果word文档是2016或者2017版本的话,可以直接用我压缩包的插件,安装即可。

    在这里插入图片描述

    7、打开之后是这个样子,选择连接数据库

    在这里插入图片描述

    8、接下来按照图片来进行操作:

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    9、导出成功,效果是这样的,可以根据自己的需求手动调整。

    在这里插入图片描述

    结语

    以上是导出的全部操作,清晰明了。这个工具对于导出表结构文档还是很有帮助的,样式虽然不好看,不过基本也描述的挺清楚的,避免了一个一个新建表格。

    展开全文
  • SpringMVC中使用FreeMarker生成Word文档

    万次阅读 2017-02-03 17:02:49
    这实例主要是完成使用FreeMarker在SpringMVC框架中生成word文档下载。 1、实例是maven工程,工程对jar包的依赖,pom.xml中主要的依赖是对springMVC和freemarker的包依赖: < properties > <!-- spring...
  •  最近项目中有一个这样的需求:导出word 文档,要求这个文档的格式不是固定的,用户可以随便的调整,导出内容中的数据表格列是动态的,例如要求导出姓名和性别,你就要导出这两列的数据,而且这个文档不是导出来...
  • word中如何在指定页开始顺序插入页码? ** 原料: word2019 实现的关键步骤: (1)菜单栏的【布局】丨【分隔符】,在指定页下方插入一个分页符,目的是为了断开页眉页脚的链接,这样顺序插入页码时就不会发生连锁...
  • DL4J中文文档/语言处理/Word2Vec

    千次阅读 2018-11-01 18:54:41
    Word2Vec, Doc2vec &amp; GloVe: 用于自然语言处理的神经词嵌入 内容 介绍 神经词嵌入 有趣的Word2Vec结果  给我代码 Word2Vec 剖析 安装,加载与训练 代码示例 问题排查与Word2Vec调试 Word2Vec用例...
  • 当希望Word表格中序号列的内容能够像Excel一样,根据行顺序自动排列时,可以在“引用”-“插入题注”中,...如果要排列序号,全选文档后选择“更新域”,序号根据行顺序排列。 转载于:https://blog.51cto.com/...
  • mac:文件编码问题

    千次阅读 2019-04-24 16:01:42
    Office for Mac 打开 Excel 文件全是乱码? 原因:Mac默认的编码是utf-8,utf-8编码的文件,用utf-8...解决方案1:新建excel空白文档,文件>导入>csv文件,文件格式选择 文件正确的编码,如:unicode(utf-...
  • word技巧

    千次阅读 2018-03-01 21:40:22
    word 高效经典教程(整理版)目录一分钟驾驭word 高效经典教程(整理版)... 6A、基础知识... 61、度量单位... 62、WORD中文字字号与磅的对应关系... 63、字体文件格式...... 96、Word文档超级链接知多...
  • word2vec——高效word特征求取

    万次阅读 2015-06-07 16:07:36
    继上次分享了经典统计语言模型,最近公众号中有很多做NLP朋友问到了关于word2vec的相关内容, 本文就在这里整理一下做以分享。 本文分为 概括word2vec 相关工作 模型结构 Count-based方法 vs. Directly predict 几...
  • word设置多级目录

    千次阅读 2020-07-12 00:00:19
     作为一个优秀的程序员,日常的工作除了编码开发和测试之外,还有一项无法避免的,而且也占据巨大的工作量的内容就是文档的编制,因为 一个软件或者产品的质量,不是开发出来的,而是管理出来的 而管理的内容,不...
  • 笔者寄语:2013年末,Google发布的 word2vec工具引起了一帮人的热捧,大家几乎都认为它是深度学习在自然语言领域的...但是doc2vec不仅考虑了单词上下文的语义,还考虑了单词在段落中的顺序。 ———————————
  • word2vec和word embedding有什么区别?

    千次阅读 2017-12-18 11:26:29
    作者:Scofield链接:...很好,正好可借此机会介绍词向量、word2vec以及DeepNLP整套相关的东西:文章很长,是从CSDN上写好复制过来的,亦可直接跳到博客观看:深度学习(Deep Learning)
  • java编码规范

    千次阅读 2018-06-06 16:53:53
    是从我的word文档里面直接拷贝出来的,某些地方排版好像不太对,懒得修改了,有不对的地方请指出,看到的话就会修改的。仅供参考,请勿盗取,谢谢。Java开发规范1. 引言 22.文件 23.命名规则 33.1基本的规则 33.2...
  • 编码

    千次阅读 2008-01-31 13:54:00
    http://kyxk.net/pc/pccon.php?id=126&nid=5504(较长) 一、ASCII编码 ASCII(American Standard Code for Information Interchange,美国标准信息交换码)...ASCII码有7位编码和8位编码两种形式。7位编码表示128个符
  • CSS编码规范

    千次阅读 2019-11-18 13:39:40
    CSS编码规范 转载自:https://github.com/ZXinL-CN/spec/blob/master/css-style-guide.md 1 前言 2 代码风格 2.1 文件 2.2 缩进 2.3 空格 2.4 行长度 2.5 选择器 2.6 属性 3 通用 3.1 选择器 3.2 属性...
  • Word 2003域应用完全手册

    千次阅读 2014-01-24 19:50:08
     简单地讲,域就是引导Word文档中自动插入文字、图形、页码或其他信息的一组代码 。每个域都有一个唯一的名字,它具有的功能与Excel中的函数非常相似。下面以Seq和Dat e域为例,说明有关域的一些基本概念。 ...
  • 转载请注明出处:...养成一个良好编码规范的习惯,一是可以统一代码风格;二是便于团队成员协作开发;三是在review代码的时候,更加容易精确地判断需要修改的地方;四是提高
  • 需求文档

    千次阅读 多人点赞 2019-03-29 18:10:18
    产品需求文档的表现形式有很多种,常见的有Word、图片和交互原型这三种形式,文档内容通常包含信息结构图、界面线框图、功能流程图、功能说明文档。虽然产品需求文档没有标准的规范,但是有两项是必不可少的,那就是...
  • Dspace系统文档(选译)

    千次阅读 2010-11-11 15:08:00
    <br /> <br />一、 简介  Dspace是... Dspace系统文档对系统功能作了一个整体的描述。它对Dspace系统进行了一个良好的性能描述,为非技术人员学习使用Dspace系统是一个良好的导向。在阅读整个系
  • Bibtex4Word+Latex+Jabref实现Word参考文献插入和排版
  • 昨天测试一个接口的时候发现一个异常,浏览器访问一个controller,然后带了一个JSON格式的...该文件的字符编码需要在传输协议层声明,或者在文件中加入一个 BOM(字节顺序标记)。 很纳闷,这个服务的部署环境是windo
  • 字符编码(一)

    千次阅读 2014-03-10 21:16:16
    在写文档的时候,拷贝程序中的代码至Word中,发现了下图所示的问题:乱码。 图1 乱码问题示意图 联想到先前在工作中也出现过类似的情况:某些.h或者.cpp文件打开后中文的注释显示一些很奇怪字符,而代码...
  • 首先简单介绍一下香农编码的方法步骤: 因为有公式,为了省事,所以就先在word文档中写好了,直接截图,如下:
  • HTML代码转word!亲测!可用!!!

    千次阅读 热门讨论 2019-10-31 14:48:34
    用户点击保存按钮,需要导出为word文档。 因为现在接手的项目,是基于上一个项目的框架。两个项目功能点差不多。但是在导出word这块,是后台java做的。也就是用的POI。前端传递参数至后台,后台将word模板中的$...
  • 中文编码杂谈

    千次阅读 2013-11-11 17:33:12
    编码问题的例子 在windows自带的notepad(记事本)程序中输入“联通”两个字,保存后再次打开,会发现“联通”不见了,代之以“��ͨ”的乱码。这是windows平台上典型的中文编码问题。即文件保存的时候是按照ANSI...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 25,405
精华内容 10,162
关键字:

word文档顺序编码