1 . 实现代码
package com.base.pf.common.util; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.axis.encoding.Base64; import com.base.pf.base.util.PropertiesMgr; import com.base.pf.base.util.StringUtils; /** * 导出WORD * * @author ZHEN.L * @date 2014.09.23 * @date 2014.11.05 * */ public class WordUtils { /** * 转换PDF格式 */ public static final String WORD_2_PDF = "Word.pdf"; /** * word导出标记 */ public static final String WORD_EXP_TAG = "Word.exp"; /** * word导出值 */ public static final String WORD_EXP_TAG_VALUE = "word"; /** * 模板名称标记 */ public static final String WORD_TEMPLATE_NAME = "Word.fileName"; /** * 模板名称标记 */ public static final String WORD_FILE_NAME = "fileName"; /** * 模板名称标记:动态字段 */ public static final String WORD_FILE_NAME_FIELD = "FieldfileName"; // 模板路径 protected static final String WORD_TEMPLATE_PATH = "\\page\\project\\template_word\\"; /** * word中换行:需要将word中\r\n转回为以下字符串 <w:spacing w:line="240" w:line-rule="auto" * /> 240为单倍行距,360为1.5倍行距 */ protected static final String LINE_FEED = "</w:t></w:r></w:p><w:p wsp:rsidR=\"004C3B63\" wsp:rsidRDefault=\"004C3B63\" wsp:rsidP=\"00302FB9\"><w:pPr><w:spacing w:line=\"340\" w:line-rule=\"auto\" /><w:rPr><w:rFonts w:ascii=\"宋体\" w:h-ansi=\"宋体\"/><wx:font wx:val=\"宋体\"/><w:sz w:val=\"24\"/></w:rPr></w:pPr><w:r><w:rPr><w:rFonts w:ascii=\"宋体\" w:h-ansi=\"宋体\" w:hint=\"fareast\"/><wx:font wx:val=\"宋体\"/><w:sz w:val=\"24\"/></w:rPr><w:t> "; protected static final String PDF_PATH = PropertiesMgr .getValue("exp_word_pdf_path") + "/"; /** * 导出word * * @param response */ public synchronized static String exp(HttpServletRequest request, HttpServletResponse response, Object obj) { String xml = ""; String PDF = request.getParameter(WORD_2_PDF); Map map = BeanToMapUtils.convertBean(obj); String templatePath = request.getServletContext().getRealPath("/") + WORD_TEMPLATE_PATH; String templateName = request.getParameter(WORD_TEMPLATE_NAME); // 模板名称,程序中获取,通过DTO传递过来 if (map.get("templateName") != null) { templateName = String.valueOf(map.get("templateName")); } File file = new File(templatePath + templateName); String fileName = request.getParameter(WORD_FILE_NAME); String fieldFileName = request.getParameter(WORD_FILE_NAME_FIELD); if (!file.exists()) { throw new NullPointerException("模板不存在!"); } OutputStream os = null; BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader(new FileInputStream( file), "UTF-8")); String buf = null; while ((buf = br.readLine()) != null) { xml += buf; } // ContactDtoT dto = new ContactDtoT(); // if (!(obj instanceof Map)) // BeanUtils.copyProperties(obj, dto); if (!StringUtils.isEmpty(fieldFileName)) { String temp = String.valueOf(map.get(fieldFileName)); if (!StringUtils.isEmpty(temp)) { fileName = temp.trim() + fileName; } } xml = replace(xml, map); if (!"word".equalsIgnoreCase(PDF)) { File pdfFile = new File(PDF_PATH); if (!pdfFile.exists()) { pdfFile.mkdirs(); } OutputStream localOs = new FileOutputStream(new File(PDF_PATH + fileName + ".doc")); localOs.write(xml.getBytes("UTF-8")); PDFUtils.download(request, response, PDF_PATH + fileName + ".doc"); return null; } response.setContentType("application/msword,charset=UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".doc", "UTF-8")); os = response.getOutputStream(); os.write(xml.getBytes("UTF-8")); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { close(br, os); } return null; } // 将文件,转换成BASE64 protected static String getBASE64(String path) { // path = "d:/1.jpg"; String base64 = ""; File file = new File(path); FileInputStream fis = null; ByteArrayOutputStream bos = null; try { fis = new FileInputStream(file); bos = new ByteArrayOutputStream(1000); byte[] bytes = new byte[1024]; while ((fis.read(bytes)) != -1) { bos.write(bytes); } base64 = Base64.encode(bos.toByteArray()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { close(fis, bos); } return base64; } // 替换xml中字符 protected static String replace(String xml, Map map) { Integer pWidth = 0, pHeight = 0; // 印章高度、宽度 Set set = map.keySet(); int photoIndex = 0; // 图片序号标记 Iterator its = set.iterator(); String key, value, tagKey, photoXml; // 图片部分XML while (its.hasNext()) { try { key = StringUtils.withSpaceReplaceNull(its.next()); tagKey = "${" + key + "}"; value = StringUtils.withSpaceReplaceNull(map.get(key)); if (key.startsWith("photo_")) { photoIndex++; ImageDto imageDto = readImageInfo(value); // 设计思想: 首先将XML中图片标记${key}替换为word图片对应的XML; // 第二,将${photo_tag}图片(电子印章)替换为对应的${key}值,再将${key}替换为base64, // 公司公章:大部分长度4.2cm,个别长度3.8 if ("photo_superName".equalsIgnoreCase(key) || "photo_super".equalsIgnoreCase(key)) { // 图片位置 String pict_position_left = StringUtils .withSpaceReplaceNull(map .get("pict_position_left")); String pict_position_top = StringUtils .withSpaceReplaceNull(map .get("pict_position_top")); pict_position_left = "".equals(pict_position_left) ? "0" : pict_position_left; pict_position_top = "".equals(pict_position_top) ? "-47" : pict_position_top; photoXml = IMG_UP.replace("${pict_index}", // 替换图片序列号 String.valueOf(photoIndex)); photoXml = photoXml.replace("${pict_position_left}", pict_position_left); // 替换图片左侧位置 photoXml = photoXml.replace("${pict_position_top}", pict_position_top); // 替换图片上部位置 // 像素和厘米之间的换算是需要知道图片的分辨率的。通常设计网页的时候,图片的分辨率一般都是用72dpi的, // 即72像素/英寸,由于1英寸= 2.54厘米,所以在设计网页的时候,一般1厘米约为28像素 imageDto.getWidth(); photoXml = photoXml.replace("${pict_width}", getpWidth(key, null, imageDto.getWidth())); // 替换图片宽度 photoXml = photoXml.replace("${pict_height}", getpHeight(key, null, imageDto.getHeight())); // 替换图片高度 photoXml = photoXml.replace("${pict_tag}", tagKey); xml = xml.replace(tagKey, photoXml); // 个人签章:个人签名(高1.8cm,宽待定)、执业资格章(高3cm,宽4.5cm) } else if ("photo_engineer".equalsIgnoreCase(key) || "photo_chief".equalsIgnoreCase(key)) { String pict_position_left = StringUtils .withSpaceReplaceNull(map .get("pict_position_left_personal")); String pict_position_top = StringUtils .withSpaceReplaceNull(map .get("pict_position_top_personal")); pict_position_top = "".equals(pict_position_top) ? "0" : pict_position_top; pict_position_left = "".equals(pict_position_left) ? "0" : pict_position_left; photoXml = IMG_UP.replace("${pict_index}", String.valueOf(photoIndex)); photoXml = photoXml.replace("${pict_position_left}", pict_position_left); // 替换图片左侧位置 photoXml = photoXml.replace("${pict_position_top}", pict_position_top); // 替换图片上部位置 photoXml = photoXml.replace( "${pict_width}", getpWidth(key, map.get("personalSealType"), imageDto.getWidth())); // 替换图片宽度 photoXml = photoXml.replace( "${pict_height}", getpHeight(key, map.get("personalSealType"), imageDto.getHeight())); // 替换图片高度 photoXml = photoXml.replace("${pict_tag}", tagKey); xml = xml.replace(tagKey, photoXml); } xml = xml.replace(tagKey, imageDto.getBase64()); } else { value = value.replace("\r\n", LINE_FEED); xml = xml.replace(tagKey, value); } } catch (Exception e) { e.printStackTrace(); } } String date = new SimpleDateFormat("yyyy 年 MM 月 dd 日") .format(new Date()); xml = xml.replace("${date}", date); xml = xml.replace("${photo_}", ""); xml = xml.replace("${photo_superName}", ""); xml = xml.replace("${photo_super}", ""); xml = xml.replace("${photo_engineer}", ""); xml = xml.replace("${photo_chief}", ""); return xml; } // 获取印章宽度: 个人签名:高度1.8cm;个人执业资格:高度3cm,宽度:4.5cm; 公司公章:最大4.2cm private static String getpWidth(String pType, Object sealPeronal, double width) { double pWidth = 100; String sealPeronalType = String.valueOf(sealPeronal); // 公章 if ("photo_superName".equalsIgnoreCase(pType) || "photo_super".equalsIgnoreCase(pType)) { pWidth = width > 119 ? 119 : width; // 个人签章:执业资格 } else if ("photo_engineer".equalsIgnoreCase(pType) && "1001".equals(sealPeronalType)) { pWidth = width > 126 ? 126 : width; // 个人签章:签名 } else if ("photo_engineer".equalsIgnoreCase(pType)) { pWidth = width > 195 ? 195 : width; // 个人签章:执业资格 } else if ("photo_chief".equalsIgnoreCase(pType) && "1001".equals(sealPeronalType)) { pWidth = width > 126 ? 126 : width; // 个人签章:签名 } else if ("photo_chief".equalsIgnoreCase(pType)) { pWidth = width > 202 ? 202 : width; } return String.valueOf(pWidth); } // 获取印章宽度: 个人签名:高度1.8cm;个人执业资格:高度3cm,宽度:4.5cm; 公司公章:最大4.2cm private static String getpHeight(String pType, Object sealPeronal, double height) { double pHeight = 100; String sealPeronalType = String.valueOf(sealPeronal); // 公章 if ("photo_superName".equalsIgnoreCase(pType) || "photo_super".equalsIgnoreCase(pType)) { pHeight = height > 119 ? 119 : height; // 个人签章:执业资格 } else if ("photo_engineer".equalsIgnoreCase(pType) && "1001".equals(sealPeronalType)) { pHeight = height > 84 ? 84 : height; // 个人签章:签名 } else if ("photo_chief".equalsIgnoreCase(pType) && "1001".equals(sealPeronalType)) { pHeight = height > 84 ? 84 : height; } else if ("photo_engineer".equalsIgnoreCase(pType) || "photo_chief".equalsIgnoreCase(pType)) { pHeight = height > 50.4 ? 50.4 : height; } return String.valueOf(pHeight); } /** * 读取图片信息 * * @date 2014.10.22 * @param path */ protected static ImageDto readImageInfo(String path) { // path = "d:/b3.jpg"; if (StringUtils.isEmpty(path)) { throw new NullPointerException("图片路径为空!"); } File file = new File(path); if (!file.exists()) { throw new NullPointerException("图片不存在,路径为:" + path); } ImageDto dto = null; BufferedImage bi = null; ByteArrayOutputStream bos = null; FileInputStream fis = null; try { dto = new ImageDto(); fis = new FileInputStream(file); bos = new ByteArrayOutputStream(1000); byte[] bytes = new byte[1024]; while ((fis.read(bytes)) != -1) { bos.write(bytes); } bi = ImageIO.read(file); dto.setWidth(bi.getWidth()); dto.setHeight(bi.getHeight()); dto.setBase64(Base64.encode(bos.toByteArray())); } catch (IOException e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (bos != null) { try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } } return dto; } // 关闭IO protected static void close(BufferedReader br, OutputStream os) { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if (os != null) { try { os.flush(); } catch (IOException e) { e.printStackTrace(); } try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } // 关闭IO protected static void close(FileInputStream fis, ByteArrayOutputStream bos) { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if (bos != null) { try { bos.flush(); } catch (IOException e) { e.printStackTrace(); } try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 签名:在文字的右侧 */ protected static final String IMG_RIGHT = "" + " <w:pict> " + " <v:shapetype id=\"_x0000_t75\" coordsize=\"21600,21600\" o:spt=\"75\" " + " o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\"> " + " <v:stroke joinstyle=\"miter\" /> " + " <v:formulas> " + " <v:f eqn=\"if lineDrawn pixelLineWidth 0\" /> " + " <v:f eqn=\"sum @0 1 0\" /> " + " <v:f eqn=\"sum 0 0 @1\" /> " + " <v:f eqn=\"prod @2 1 2\" /> " + " <v:f eqn=\"prod @3 21600 pixelWidth\" /> " + " <v:f eqn=\"prod @3 21600 pixelHeight\" /> " + " <v:f eqn=\"sum @0 0 1\" /> " + " <v:f eqn=\"prod @6 1 2\" /> " + " <v:f eqn=\"prod @7 21600 pixelWidth\" /> " + " <v:f eqn=\"sum @8 21600 0\" /> " + " <v:f eqn=\"prod @7 21600 pixelHeight\" /> " + " <v:f eqn=\"sum @10 21600 0\" /> " + " </v:formulas> " + " <v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\" /> " + " <o:lock v:ext=\"edit\" aspectratio=\"t\" /> " + " </v:shapetype> " + " <w:binData w:name=\"wordml://03000002.png\" xml:space=\"preserve\">${photo_tag}</w:binData> " + " <v:shape id=\"图片 1\" o:spid=\"_x0000_i1025\" type=\"#_x0000_t75\" " + " style=\"margin-top:10pt;width:106.5pt;height:48pt;z-index:1;visibility:visible;mso-wrap-style:square\"> " + " <v:imagedata src=\"wordml://03000002.png\" o:title=\"\" /> " + " </v:shape> " + " </w:pict> "; /** * 位置:在图片的上方 * */ protected static final String IMG_UP = "" + " <w:pict> " + " <v:shapetype id=\"_x0000_t75\" coordsize=\"21600,21600\" o:spt=\"75\" " + " o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\"> " + " <v:stroke joinstyle=\"miter\" /> " + " <v:formulas> " + " <v:f eqn=\"if lineDrawn pixelLineWidth 0\" /> " + " <v:f eqn=\"sum @0 1 0\" /> " + " <v:f eqn=\"sum 0 0 @1\" /> " + " <v:f eqn=\"prod @2 1 2\" /> " + " <v:f eqn=\"prod @3 21600 pixelWidth\" /> " + " <v:f eqn=\"prod @3 21600 pixelHeight\" /> " + " <v:f eqn=\"sum @0 0 1\" /> " + " <v:f eqn=\"prod @6 1 2\" /> " + " <v:f eqn=\"prod @7 21600 pixelWidth\" /> " + " <v:f eqn=\"sum @8 21600 0\" /> " + " <v:f eqn=\"prod @7 21600 pixelHeight\" /> " + " <v:f eqn=\"sum @10 21600 0\" /> " + " </v:formulas> " + " <v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\" /> " + " <o:lock v:ext=\"edit\" aspectratio=\"t\" /> " + " </v:shapetype> " + " <w:binData w:name=\"wordml://0300000${pict_index}.png\" xml:space=\"preserve\">${pict_tag}</w:binData> " + " <v:shape id=\"图片 3\" o:spid=\"_x0000_s1026\" type=\"#_x0000_t75\" " + " style=\"position:absolute;left:0;text-align:left;margin-left:${pict_position_left}pt;margin-top:${pict_position_top}pt;width:${pict_width}pt;height:${pict_height}pt;z-index:1;visibility:visible;mso-wrap-style:square;mso-width-percent:0;mso-height-percent:0;mso-wrap-distance-left:9pt;mso-wrap-distance-top:0;mso-wrap-distance-right:9pt;mso-wrap-distance-bottom:0;mso-position-horizontal:absolute;mso-position-horizontal-relative:text;mso-position-vertical:absolute;mso-position-vertical-relative:text;mso-width-percent:0;mso-height-percent:0;mso-width-relative:margin;mso-height-relative:margin\"> " + " <v:imagedata src=\"wordml://0300000${pict_index}.png\" o:title=\"\" /> " + " </v:shape> " + " </w:pict> "; } 2. 在具体需要导出的功能的查询单条记录的方法中加入代码
// 导出word if (WordUtils.WORD_EXP_TAG_VALUE.equals(request .getParameter(WordUtils.WORD_EXP_TAG))) { WordUtils.exp(request, response, dto); return null; } 3. 导出页面
<!-- 导出WORD --> <div><form id="WordForm" name="WordForm" method="post" action="query.do"> <!-- 查询路径 --> <input type="hidden" name="<%=WordUtils.WORD_EXP_TAG %>" value="<%=WordUtils.WORD_EXP_TAG_VALUE %>"/><!-- 用于判断是否为导出功能,无需修改 --> <input type="hidden" name="<%=WordUtils.WORD_TEMPLATE_NAME %>" value="start.xml"/> <!-- 模板名称:模板放在page/project/template_word下 --> <input type="hidden" name="<%=WordUtils.WORD_FILE_NAME %>" value="开工令"/> <!-- 导出的WORD名称(注:系统会自动为名称后加上当前日期) --> <input type="hidden" name="id" id="id"/> <input type="hidden" name="handleType" id="handleType" value="<%=IEnginerringStartConstant.HANDLE_BROWSE%>"/> </form></div> 4. 导出js
// 导出 EnginerringStartHandle.exp = function(){ var id = $("#modifyId").val(); if(!id){ $.messager.alert('提示',"请先保存数据",'info'); return; } $("#id").val(id); $.messager.progress({ msg:'正在处理,请等待...' }); $("#WordForm").submit(); setTimeout("$.messager.progress('close')",6000); };