未优化前
@Test
public void downloadElecreicFile(HttpServletResponse response, @Param("exhibitionId") String exhibitionId){
//从数据库查询数据
List<Map<String, Object>> data = dataService.getEmsDataByConditions(paramters);
//获取上传的模板路径
Attachment attachment = attachmentService.getAttachmentMode(exhibitionId,ELECTRIC_NAME);
Map<String, Object> parametersMap = new HashMap<String, Object>();
XWPFTemplate template = null;
List<XWPFDocument> listTemplate = new ArrayList<>();
//从服务器获取模板的存储路径
File file = null;
String filename = null;
if (attachment != null) {
String filePath = attachment.getFilePath();
String realpath = uploadDir + filePath;
filename = attachment.getName();
file = new File(realpath);
}
try {
for (int i = 0; i < data.size(); i++) {
Map<String, Object> stringObjectMap = data.get(i);
parametersMap.put("gazw_",stringObjectMap.get("gazw_"));
parametersMap.put("boothNo",stringObjectMap.get("booth_no"));
parametersMap.put("companyName",stringObjectMap.get("company_name"));
parametersMap.put("iWfc_",stringObjectMap.get("iWfc_"));
parametersMap.put("telephone",stringObjectMap.get("telephone"));
parametersMap.put("email",stringObjectMap.get("email"));
parametersMap.put("address",stringObjectMap.get("address"));
parametersMap.put("JBcl_",stringObjectMap.get("JBcl_"));
template = XWPFTemplate.compile(new FileInputStream(file)).render(parametersMap);
XWPFDocument xwpfDocument = template.getXWPFDocument();
listTemplate.add(xwpfDocument);
}
}catch (Exception e) {
e.printStackTrace();
}
//循环合并多个word文档
try{
XWPFDocument xmd = listTemplate.get(0);
for (int i = 0; i < 100; i++) {
xmd = mergeWord(xmd,listTemplate.get(i+1));
}
String fileName = UUID.randomUUID().toString() + ".docx";
String filePath = "D:/coolgua/wordfile/download" + "/"+fileName;
File file1 = new File(filePath);
FileOutputStream fos = new FileOutputStream(file1);
xmd.write(fos);
fos.flush();
fos.close();
xmd.close();
}catch (Exception e){
e.printStackTrace();
}
}
//两个对象进行追加
public XWPFDocument mergeWord(XWPFDocument document,XWPFDocument doucDocument2) throws Exception {
XWPFDocument src1Document =document ;
XWPFParagraph p = src1Document.createParagraph();
//设置分页符
p.setPageBreak(true);
CTBody src1Body = src1Document.getDocument().getBody();
XWPFDocument src2Document = doucDocument2;
CTBody src2Body = src2Document.getDocument().getBody();
// XWPFParagraph p2 = src2Document.createParagraph();
XmlOptions optionsOuter = new XmlOptions();
optionsOuter.setSaveOuter();
String appendString = src2Body.xmlText(optionsOuter);
String srcString = src1Body.xmlText();
String prefix = srcString.substring(0,srcString.indexOf(">")+1);
String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
String sufix = srcString.substring( srcString.lastIndexOf("<") );
String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
CTBody makeBody = CTBody.Factory.parse(prefix+mainPart+addPart+sufix);
src1Body.set(makeBody);
return src1Document;
}
用过循环遍历集合合并
XWPFDocument xmd=list.get(0); //默认获取第一个作为模板
for (int i=0;i<list.size()-1;i++) {
xmd=mergeWord(xmd,list.get(i+1)); //相继合并
}
缺点:只适用于数据量较小的word,数据量过大时,合并超过两百个word文档时就容易造成堆内存溢出;因为我们看这个循环遍历调用mergeWord()方法,mergeWord里边创造了大量的对象,其中明显最多的就是String对象、CTBody对象,循环调用造成这些对象越来越大,以至于在年轻代的伊甸区放不下,一被创建就直接进入了老年区,最终造成老年区也溢出,报堆内存溢出;还有parametersMap、XWPFTemplate 、listTemplate 这几个存储大数据的对象都放到外边,循环的话不会释放掉它的内存。
优化后1
/**
* 对多个word文档进行拼接
* @param document
* @param doucDocument2
* @return
* @throws Exception
*/
public XWPFDocument mergeWord(XWPFDocument document, List<XWPFDocument> doucDocument2) throws Exception {
XWPFDocument src1Document =document ;
CTBody src1Body = src1Document.getDocument().getBody();
XmlOptions optionsOuter = new XmlOptions();
optionsOuter.setSaveOuter();
String srcString = src1Body.xmlText();
String prefix = srcString.substring(0,srcString.indexOf(">")+1);
String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
String sufix = srcString.substring( srcString.lastIndexOf("<") );
StringBuilder sb = new StringBuilder();
sb.append(prefix);
sb.append(mainPart);
for (XWPFDocument doc : doucDocument2) {
CTBody src2Body = doc.getDocument().getBody();
String appendString = src2Body.xmlText(optionsOuter);
src2Body = null;
String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
appendString = null;
sb.append(addPart);
addPart = null;
doc.close();
}
//将两个文档的xml内容进行拼接;拼接文档过大的话,容易造成堆内存溢出
CTBody makeBody = CTBody.Factory.parse(sb.append(sufix).toString());
src1Body.set(makeBody);
return src1Document;
}
合并
XWPFDocument xmd = null;
xmd = listTemplate.size() > 1 ? mergeWord(listTemplate.get(0), listTemplate.subList(1, listTemplate.size())) : listTemplate.get(0);
这里我们主要使用了StringBuilder来解决循环合并时,String字符串的拼接问题。而且把CTBody这个大对象放到了循环体之外,效果是可以合并带有图像的word文档,大概两千个左右;
缺点:问题是使用poi合并word,始终避免不了合并的两个word文档越来越大时,所占的内存会越来越大。
最终的优化方案
/**
* 下载电子会刊
* @param response
* @param exhibitionId
*/
@RequestMapping("/downloadElecreicFile")
public void downloadElecreicFile(HttpServletResponse response,@Param("exhibitionId") String exhibitionId){
//1:根据条件获取对应表单
Map<String, String> param = Maps.newHashMap();
param.put("formType", "24");
param.put("exhibitionId", exhibitionId);
Form form =formMapper.getFormByFormType(param).get(0);
//2:查询审核通过的对应展商的电子会刊表单
Map<String, Object> paramters = new HashMap<String, Object>();
paramters.put("exhibitionId", exhibitionId);
paramters.put("formId", form.getId());
paramters.put("aduitStatus",AUDIT_STATUS_PASS);
List<Map<String, Object>> data = dataService.getEmsDataByConditions(paramters);
//获取上传的模板路径
Attachment attachment = attachmentService.getAttachmentMode(exhibitionId,ELECTRIC_NAME);
File file = null;
String filename = null;
//从服务器获取模板的存储路径
if (attachment != null) {
String filePath = attachment.getFilePath();
String realpath = uploadDir + filePath;
filename = attachment.getName();
file = new File(realpath);
}
try {
Map<String, Object> parametersMap = new HashMap<String, Object>();
Map<String, Object> dataMap1 = data.get(0);
replace(parametersMap,dataMap1);
XWPFTemplate template = XWPFTemplate.compile(new FileInputStream(file)).render(parametersMap);
XWPFDocument src1Document = null;
src1Document = template.getXWPFDocument();
CTBody src1Body = src1Document.getDocument().getBody();
XmlOptions optionsOuter = new XmlOptions();
optionsOuter.setSaveOuter();
String srcString = src1Body.xmlText();
String prefix = srcString.substring(0,srcString.indexOf(">")+1);
String mainPart = srcString.substring(srcString.indexOf(">")+1,srcString.lastIndexOf("<"));
String sufix = srcString.substring( srcString.lastIndexOf("<") );
StringBuilder sb = new StringBuilder();
sb.append(prefix);
sb.append(mainPart);
for (int i = 1; i < data.size(); i++) {
Map<String, Object> dataMap2 = data.get(i);
Map<String, Object> parametersMap2 = new HashMap<String, Object>();
replace(parametersMap2,dataMap2);
//按占位符替换会刊模板的相应数据
XWPFTemplate template1 = XWPFTemplate.compile(new FileInputStream(file)).render(parametersMap2);
XWPFDocument xwpfDocument2 = template1.getXWPFDocument();
CTBody src2Body = xwpfDocument2.getDocument().getBody();
String appendString = src2Body.xmlText(optionsOuter);
src2Body = null;
String addPart = appendString.substring(appendString.indexOf(">") + 1, appendString.lastIndexOf("<"));
appendString = null;
sb.append(addPart);
addPart = null;
xwpfDocument2.close();
xwpfDocument2 = null;
}
//此对象过大的话,会直接进入老年代,,所以不要放进循环体内。
CTBody makeBody = CTBody.Factory.parse(sb.append(sufix).toString());
src1Body.set(makeBody);
makeBody = null;
//输出流下载
response.reset();
response.setContentType("application/x-msdownload");
String fileName = new String(filename.getBytes(), "ISO8859-1");
response.setCharacterEncoding("utf-8");
response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
OutputStream os = new BufferedOutputStream(response.getOutputStream());
src1Document.write(os);
os.flush();
os.close();
src1Document.close();
src1Document = null;
}catch (IOException | XmlException e) {
e.printStackTrace();
}
}
/**
* 替换模板占位符
* @param parametersMap
* @param dataMap
*/
private void replace(Map<String, Object> parametersMap,Map<String, Object> dataMap){
parametersMap.put("gazw_",dataMap.get("gazw_"));
parametersMap.put("boothNo",dataMap.get("booth_no"));
parametersMap.put("companyName",dataMap.get("company_name"));
parametersMap.put("iWfc_",dataMap.get("iWfc_"));
parametersMap.put("telephone",dataMap.get("telephone"));
parametersMap.put("email",dataMap.get("email"));
parametersMap.put("address",dataMap.get("address"));
parametersMap.put("JBcl_",dataMap.get("JBcl_"));
//从服务器获取图片流
String tDuI_ = (String) dataMap.get("TDuI_");
if (tDuI_!=null){
Attachment attachment2 = attachmentService.getAttachmentById(tDuI_);
if (attachment2!=null){
String picFilePath = attachment2.getFilePath();
String picRealPath = uploadDir + picFilePath;
String suffix = attachment2.getSuffix();
File file1 = new File(picRealPath);
try {
parametersMap.put("img",new PictureRenderData(80,100,suffix,new FileInputStream(file1)));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
}