您的位置:首页 > 其它

使用Velocity+zip+xml快速构建word2007文档

2010-05-05 10:43 330 查看
技术介绍

     在一般的Java项目中,生成word文档的时候,我们会使用到jacob来作为我们生成word文档的工具,但是jacob是通过使用JNI调用dll文件来实现,这样的工作方式带来了极大的性能开销.这里我们采用Velocity+zip+xml快速构建word2007文档,所以说这个技术实现有一定的局限性,生成的必须是word2007文档.

     我们先来了解下word2007文档,word2007文档本身是以xml文件格式存储的,然后将相关的xml文件和资源文件压缩得到.为了让大家更加明白,先来做个小实验,我们新建一个word2007文档,然后打开后随便输入一些内容,保存后,将其扩展名docx修改为zip.然后你会发现真的可以用压缩工具查看.目录结构如下.

│  [Content_Types].xml

├─docProps
│      app.xml
│      core.xml

├─word
│  │  document.xml
│  │  endnotes.xml
│  │  fontTable.xml
│  │  footnotes.xml
│  │  settings.xml
│  │  styles.xml
│  │  webSettings.xml
│  │
│  ├─theme
│  │      theme1.xml
│  │
│  └─_rels
│          document.xml.rels

└─_rels
        .rels

 

     这里文件比较多,但是我们需要关心的文件只有document.xml文件.这样我们就将构建word2007文档的问题转化成修改一个压缩文件中的xml问题,所以我们会使用到zip来处理压缩文件,由于对document.xml文档的定义比较复杂,这里我们采用模板的方式来修改document.xml文档,所以我们用到了强大的模板引擎,velocity.

 

    基本上用到的技术和实现策略已经介绍得差不多了,下面我们看看如何实现.

 

第一步:创建模板

新建一个目录,我们这里命名为mytemplate,

在目录下新建一个word2007文档,输入下面内容

 

姓名:${name}

性别:${sex}

 

    保存文章内容后,将其扩展名改为zip,使用压缩工具打开,将其中的document.xml文件解压一份到当前文件夹下的word目录.(注意不要将所有文件解压,只解压一个文件即可,当所有文件解压后,重新压缩后,word会无法解析,具体原因还不是很清楚)

然后从压缩文件中删除docuemnt.xml,

然后将压缩文件名修改为template.docx

看下目录结构

mytemplate
│  template.docx

└─word
    │  document.xml
    │
    └─temp

第一步:编写代码

 

创建工具代码

 

 

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.nio.channels.FileChannel;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

/**
 * 通过word模板生成word文件的类
 * @author wangchao
 * Create Date 2010-4-29
 */
public class Word07Tools {
 
 private static String SPLITER = "/";
 private static String WORD = "word";
 private static String DOCUMENT = "document.xml";
 private static String ENCODING = "UTF-8";
 private static String LODERPATH = "file.resource.loader.path";
 private static String TEMPDIR = "/temp";
 private static String TEMPLATEFILENAME = "template.docx";
 
 /**
  * 使用word文件模板生成word文件
  * @param dataMap 数据
  * @param templatePath 模板文件路径
  * @param outFilePath 目标文件
  * @throws Exception
  */
 public static synchronized void createWord(HashMap<String,Object> dataMap,String templatePath,String outFilePath) throws Exception{
  if(templatePath!=null&&!templatePath.endsWith(SPLITER))
  {
   templatePath+=SPLITER;
  }
  //定义加入数据后的document.xml文件
  File documentFile =  new File(injectData(dataMap,templatePath));
  //定义目标文件
  File targetFile = new File(outFilePath);
  //定义模板文件
  File templateFile = new File(templatePath+TEMPLATEFILENAME);
  if(targetFile.exists()){
   targetFile.delete();
  }
  //将模板文件拷贝一份作为目标文件
  fileCopy(templateFile,targetFile);
  //在目标文件中加入新的document.xml文件
  addDocumentToTemplate(targetFile,documentFile);
  //删除临时的document.xml文件
  documentFile.delete();
 }
 
 /**
  * 想模板装入数据的方法
  * @param dataMap 要装入的数据
  * @param templatePath 模板路径
  * @return 返回装入数据完成的xml文件路径
  * @throws Exception
  */
 private static String injectData(HashMap<String,Object> dataMap, String templatePath)throws Exception {
  
  //Init Velocity
  Properties p = new Properties();
  p.setProperty(LODERPATH, templatePath+WORD);
  Velocity.init(p);
  
  VelocityContext context = new VelocityContext();
  //Build Velocity Context
  for (Iterator<Map.Entry<String,Object>> iter = dataMap.entrySet().iterator(); iter.hasNext();) {  
      Map.Entry<String,Object> entry = (Map.Entry<String,Object>) iter.next();  
      context.put(entry.getKey(), entry.getValue());
  }
  
  Template template = null;
  template = Velocity.getTemplate(DOCUMENT, ENCODING);
  String templateDocPath = templatePath+WORD+TEMPDIR+SPLITER+DOCUMENT;
  File tempdir = new File(templatePath+WORD+TEMPDIR+SPLITER);
  tempdir.mkdirs();
  OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(templateDocPath),ENCODING);
  if (template != null)
   template.merge(context, writer);
  writer.flush();
  writer.close();
  return templateDocPath;
 }

 /**
  * 将要document.xml文件的数据装入到模板中
  * @param targetFile 目标模板文件
  * @param document document.xml文件
  * @throws IOException
  */
 private static void addDocumentToTemplate(File targetFile, File document)throws IOException {
  // get a temp file
  File tempFile = File.createTempFile(targetFile.getName(), null);
  // delete it, otherwise you cannot rename your existing zip to it.
  tempFile.delete();

  boolean renameOk = targetFile.renameTo(tempFile);
  if (!renameOk) {
   throw new RuntimeException("could not rename the file " + target
4000
File.getAbsolutePath() + " to "+ tempFile.getAbsolutePath());
  }
  byte[] buf = new byte[1024];

  ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
  ZipOutputStream out = new ZipOutputStream(new FileOutputStream(targetFile));

  ZipEntry entry = zin.getNextEntry();
  while (entry != null) {
   String name = entry.getName();
   boolean notInFiles = true;
   if (document!=null) {
    if (document.getName().equals(name)) {
     notInFiles = false;
     break;
    }
   }
   if (notInFiles) {
    // Add ZIP entry to output stream.
    out.putNextEntry(new ZipEntry(name));
    // Transfer bytes from the ZIP file to the output file
    int len;
    while ((len = zin.read(buf)) > 0) {
     out.write(buf, 0, len);
    }
   }
   entry = zin.getNextEntry();
  }
  // Close the streams
  zin.close();
  // Compress the files
  if(document!=null) {
   InputStream in = new FileInputStream(document);
   // Add ZIP entry to output stream.
   out.putNextEntry(new ZipEntry(WORD+SPLITER + document.getName()));
   // Transfer bytes from the file to the ZIP file
   int len;
   while ((len = in.read(buf)) > 0) {
    out.write(buf, 0, len);
   }
   // Complete the entry
   out.closeEntry();
   in.close();
  }
  // Complete the ZIP file
  out.close();
  tempFile.delete();
 }
 
 /**
  * 文件拷贝
  * @param f1 拷贝源文件
  * @param f2 拷贝目标文件
  * @return
  * @throws Exception
  */
    private static long fileCopy(File f1,File f2) throws Exception{
         long time=new Date().getTime();
         int length=2097152;
         FileInputStream in=new FileInputStream(f1);
         FileOutputStream out=new FileOutputStream(f2);
         FileChannel inC=in.getChannel();
         FileChannel outC=out.getChannel();
         int i=0;
         while(true){
             if(inC.position()==inC.size()){
                 inC.close();
                 outC.close();
                 return new Date().getTime()-time;
             }
             if((inC.size()-inC.position())<20971520)
                 length=(int)(inC.size()-inC.position());
             else
                 length=20971520;
             inC.transferTo(inC.position(),length,outC);
             inC.position(inC.position()+length);
             i++;
         }
     }
}

第三步:编写测试代码

 

import java.util.HashMap;

public class TestWordTools {
 public static void main(String args[]) throws Exception {

  HashMap<String,Object> dataMap = new HashMap<String,Object>();
 
  dataMap.put("name", "王超");
  dataMap.put("sex", "M");
  Word07Tools.createWord(dataMap,"D:/TEMP/mytemplate/","D:/TEMP/目标.docx");
 
 }
}

 然后执行以下,可以看到,在指定的目录会生产 "目标.docx"文件

 

由于文章篇幅关系,这里只是做了一个简单的例子,对于一些复杂和循环,大家可以通过修改document.xml文件来实现.

例如需要表格,我们可以先制作一个含表格的word文档,然后取出document.xml文档进行修改.通过velocity的循环语句输出表格内容.

我这里只是抛砖引玉,希望大家能多提宝贵意见.

 

如需要源代码和实例,请发邮件给我 Email:wangchaomail@foxmail.com  

我的QQ:328054944,欢迎大家与我交流....

 

 

auth:wangchao

版权声明:欢迎转载,但是请在转载的同时在请注明出处和作者.

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息