@HUST-SuWB
2015-01-30T08:51:41.000000Z
字数 8662
阅读 404
项目实战
最近在做SSH框架下的Word文档下载,也就是导出的功能。参考了很多的资料,从POI到iText再到freemarker,poi更适合操作excel,从易用性考虑,我一开始选择了freemarker,而实际应用中,碰到了很多问题,因此记录下。
首先是freemarker的用法,有个参考资料很好用。http://www.havenliu.com/java/514.html 这里介绍得很详细,我简单说一下,基本的步骤就是
1、原始模板文档在2003版office中另存为xml文件
2、编辑此xml文件,将其中所有待插值的部分标记好(这里推荐的xml编辑软件是firstObject)
3、直接修改已经标记好后的.xml文件为.ftl文件,这就是待用的模板
4、程序调用模板,写入Map数据,生成文档
这个流程走下来,一般都不会有问题。
下面附上这种方式的代码
struts的配置就是很普通的那种,
<action name="test" class="testAction" method="test">
<result type="json"><param name="root">jsonMap</param></result>
</action>
然后是action层
//word导出
public String toWord() throws IOException{
WordExport we = null;
Applicant app = dao.query(Applicant.class, appId);
if(fileType==1){
fileFileName = "D:\\中心(助理研究员)岗位申请书.doc";
json.put("name", app.getName()!=null?app.getName():"");
json.put("gender", app.getGender()!=null?app.getGender():"");
json.put("department", app.getDepartment()!=null?app.getDepartment():"");
json.put("major", app.getMajor()!=null?app.getMajor():"");
json.put("startDate", app.getStartDate()!=null?app.getStartDate():"");
json.put("graduateDate", app.getGraduateDate()!=null?app.getGraduateDate():"");
json.put("email", app.getEmail()!=null?app.getEmail():"");
json.put("qq", app.getQq()!=null?app.getQq():"");
json.put("introducation", app.getIntroduction()!=null?app.getIntroduction():"");
json.put("studentType", app.getStudentType()!=null?app.getStudentType():"");
json.put("teacher", app.getTeacher()!=null?app.getTeacher():"");
json.put("phone", app.getMobilePhone()!=null?app.getMobilePhone():"");
json.put("address", app.getAddress()!=null?app.getAddress():"");
we = new WordExport(json, "applyWord.ftl", fileFileName);
}else if(fileType ==2){
fileFileName = "D:\\聘用协议书.doc";
json.put("name", app.getName()!=null?app.getName():"");
json.put("phone", app.getMobilePhone()!=null?app.getMobilePhone():"");
json.put("address", app.getAddress()!=null?app.getAddress():"");
we = new WordExport(json, "agreementWord.ftl", fileFileName);
}
we.createWord();
return SUCCESS;
}
再来是底层的wordExport类
package csdc.tool;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* 指定模板导出word文档
* @author suwb
*
*/
/**
* 1、将模板word文档(2003版)另存为xml文件
* 2、用firstObject打开xml文件,使用freeMarker标记(${标记})编辑其中待填入数据的地方
* 3、将该模板置于指定目录下待程序读取
* 4、确定待填入的数据和输出路径,调用程序执行
*/
public class WordExport {
private Configuration configuration = null;
private Map<String,Object> dataMap;//待填充数据
private String targetDir;//输出文件路径
private String fileName;//模板文件名
public WordExport(Map<String,Object> map, String file, String target){
configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
dataMap = map;
targetDir = target;
fileName = file;
}
public void createWord() throws IOException{
Template t=null;
//FTL文件所存在的位置
configuration.setClassForTemplateLoading(getClass(), "/csdc/tool/templates");
try {
t = configuration.getTemplate(fileName); //文件名
} catch (IOException e) {
e.printStackTrace();
}
File outFile = new File(targetDir);
Writer out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
try {
t.process(dataMap, out);
} catch (TemplateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
out.close();
}
}
}
上述方式可以实现在固定路径下的文件输出,但是我的需求是希望用户在页面上点击以后浏览器弹框出来供用户选择下载路径等,而上述方式只能在固定路径下生成文件,而且文件生成过程在用户界面上不可见,也就是说,这种方式与struts2的下载方式没有任何关系,struts2的下载是在配置文件里配置好
<result name="success" type="stream">
<param name="contentType">application/vnd.ms-word</param>
<param name="contentDisposition">attachment;filename="${filename}"</param>
<param name="inputName">downloadFile</param>
</result>
通过这种配置,并在get+"inputName"这种方式命名的方法里返回一个inputStream类型的流,浏览器就会自动识别这是个文档下载,会弹出下载框。而freemarker的流程中,我始终得不到正常的inputStream流。
为了与struts2的下载功能相结合,我首先把程序改成了如下(当然,struts2的配置如上所述)
/**
* 导出word
* @return
*/
public String excute(){
return SUCCESS;
}
/**
* 导出word
* @return 输入流
* @throws IOException
*/
public InputStream getDownloadFile() throws IOException{
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("UTF-8");
Template t=null;
//FTL文件所存在的位置
configuration.setClassForTemplateLoading(getClass(), "/csdc/tool/templates");
//增加设置忽略空值异常
// configuration.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
try {
t = configuration.getTemplate("applyWord.ftl"); //文件名
t.setEncoding("UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
Writer out = new StringWriter();
// Writer out = new BufferedWriter(new OutputStreamWriter(new ByteArrayOutputStream(), "UTF-8"));
// OutputStreamWriter out = new OutputStreamWriter(new ByteArrayOutputStream());
Map json = new HashMap();
String appId = "4028d8a449e5dc8d0149e5de2b1d0001";
Applicant app = dao.query(Applicant.class, appId);
String charest1 = "ISO8859-1";
fileFileName = "中心(助理研究员)岗位申请书.doc";
fileFileName = new String(fileFileName.getBytes(), charest1);
json.put("name", app.getName()!=null?app.getName():"");
json.put("gender", app.getGender()!=null?app.getGender():"");
json.put("department", app.getDepartment()!=null?app.getDepartment():"");
json.put("major", app.getMajor()!=null?app.getMajor():"");
json.put("startDate", app.getStartDate()!=null?app.getStartDate():"");
json.put("graduateDate", app.getGraduateDate()!=null?app.getGraduateDate():"");
json.put("email", app.getEmail()!=null?app.getEmail():"");
json.put("qq", app.getQq()!=null?app.getQq():"");
json.put("introducation", app.getIntroduction()!=null?app.getIntroduction():"");
json.put("studentType", app.getStudentType()!=null?app.getStudentType():"");
json.put("teacher", app.getTeacher()!=null?app.getTeacher():"");
json.put("phone", app.getMobilePhone()!=null?app.getMobilePhone():"");
json.put("address", app.getAddress()!=null?app.getAddress():"");
try {
t.process(json, out);
} catch (TemplateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
// out.close();
}
out.flush();
byte[] a = out.toString().getBytes();
ByteArrayInputStream b = new ByteArrayInputStream(a);
out.close();
return b;
}
在这种方式下,当我的Writer类型的变量是通过Writer out = new BufferedWriter(new OutputStreamWriter(new ByteArrayOutputStream(), "UTF-8"));或者OutputStreamWriter out = new OutputStreamWriter(new ByteArrayOutputStream());申明的时候,得到的文件永远都是打开之后只有
java.io.BufferedWriter或者java.io.OutputStreamWriter,没有其他任何内容。当我改为通过Writer out = new StringWriter();申明时,终于得到了一个大小正常的文件,但是无法打开,提示xml字符非法。我把这个doc文档重命名为xml文档打开看过,里面很多中文全部是乱码的,为了解决这个乱码我又尝试了各种棉编码解码等手段,都是失败告终。
最后,我来到了第三种思路,同样struts2配置是最简单的那种
<action name="test" class="testAction" method="test">
<result type="json"><param name="root">jsonMap</param></result>
</action>
然后action层代码是
/**
* 导出word
* @throws UnsupportedEncodingException
*/
public String test() throws UnsupportedEncodingException {
Configuration configuration = new Configuration();
configuration.setDefaultEncoding("utf-8");
Map data = new HashMap();
String appId = "4028d8a449e5dc8d0149e5de2b1d0001";
Applicant app = dao.query(Applicant.class, appId);
// String charest1 = "ISO8859-1";
// String charest2 = "utf-8";
data.put("name", app.getName()!=null?app.getName():"");
data.put("gender", app.getGender()!=null?app.getGender():"");
data.put("department", app.getDepartment()!=null?app.getDepartment():"");
data.put("major", app.getMajor()!=null?app.getMajor():"");
data.put("startDate", app.getStartDate()!=null?app.getStartDate():"");
data.put("graduateDate", app.getGraduateDate()!=null?app.getGraduateDate():"");
data.put("email", app.getEmail()!=null?app.getEmail():"");
data.put("qq", app.getQq()!=null?app.getQq():"");
data.put("introducation", app.getIntroduction()!=null?app.getIntroduction():"");
data.put("studentType", app.getStudentType()!=null?app.getStudentType():"");
data.put("teacher", app.getTeacher()!=null?app.getTeacher():"");
data.put("phone", app.getMobilePhone()!=null?app.getMobilePhone():"");
data.put("address", app.getAddress()!=null?app.getAddress():"");
configuration.setClassForTemplateLoading(getClass(), "/csdc/tool/templates");
Template t = null;
try {
t = configuration.getTemplate("applyWord.ftl");//载入模版文件(把要导出的word模版另存为word xml就可以了)
t.setEncoding("utf-8");
} catch (IOException e) {
e.printStackTrace();
}
String fileName = "中心(助理研究员)岗位申请书.doc";
fileName = new String(fileName.getBytes(), "ISO8859-1");
ActionContext ctx = ActionContext.getContext();
HttpServletResponse response = (HttpServletResponse) ctx
.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
response.setContentType("application/msword");
try {
response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
t.process(data, out);
out.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
return SUCCESS;
}
最终终于得到了正确的结果。