最近在处理 word 文档导出工作,整理并总结下。
经过一番百度和亲测,大部分人使用的都是iText,iReport等...当我去尝试用这几种方法的时候,要实现我的需求可以,但是代码量太大了~~~因为我的 word 文档结构比较复杂,内容较多,有点懒得去写。于是我寻求通过jsp 或 javascript 页面的方式导出,这样子格式都直接在web页面上已经编辑好了,不许通过代码再转成word的形式。
javascript 方式的话需要使用到ActiveXObject,这样子对浏览器是有要求的~~直接放弃
通过jsp方式的话,技术难点关键在图片的导出。百度可以知道,把图片转为base64编码直接输出至页面即可,后面就一直往这个方向走,最后结合FreeMarker,实现了需求。
下面给出步骤
建立 word 文档模板
word 文档另存为 xml 格式
在这一步,我尝试直接把xml文件放到后台,把图片base64编码通过el表达式直接替换,发现导出word文档打开会发生错误,因此,我再绕一下,把它另存为.ftl freeMarker模板
3. 编辑xml,把变量替换,改为el表达式,如这里把某图片改为el表达式
<pkg:part pkg:name="/word/media/image4.jpeg" pkg:contentType="image/jpeg" pkg:compression="store"> <pkg:binaryData>${dataPic}</pkg:binaryData> </pkg:part>
4. 把模板放到项目下,我这里以 /WEB-INF/templates
因为我结合了spring mvc 去实现,功能,因此我在 applicationContext.xml 中做好freeMarkerConfig配置如:
<!-- Freemarker 配置 --> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/templates/" /> <property name="defaultEncoding" value="UTF-8" /> <property name="freemarkerSettings"> <props> <prop key="template_update_delay">10</prop> <prop key="locale">zh_CN</prop> <prop key="number_format">0.##########</prop> <prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop> <prop key="classic_compatible">true</prop> <prop key="template_exception_handler">ignore</prop> </props> </property> </bean>
5. 添加解析 freeMarker 的 ViewResolver
<bean id="ftlViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView" /> <property name="suffix" value=".ftl" /> <property name="exposeRequestAttributes" value="true" /> <property name="exposeSessionAttributes" value="true" /> <property name="exposeSpringMacroHelpers" value="true" /> <property name="order" value="0" /> </bean>
6. 编写 controller
@RequestMapping(value = "/report") public String word(Model model) { getResponse().setCharacterEncoding("UTF-8"); getResponse().setContentType("application/msword"); getResponse().addHeader("Content-Disposition", "attachment;filename=report.doc"); // JFreeChart 图片 model.addAttribute("dataPic", Base64Utils.image2Str(ChartUtils.createLineChart(prepareDataset(), "最近7天血压数据", "时间", "mmHg"), 1200, 800)); return "report"; }
这里还提供一下图片转base64的一个工具类
public class Base64Utils { private static BASE64Encoder encoder = new BASE64Encoder(); private Base64Utils() { } /** * 本地图片转 base64 编码输出 */ public static String localImage2Str(String imagePath) { File imageFile = new File(imagePath); if (imageFile.exists() && imageFile.isFile()) { try { return image2Str(new FileInputStream(imageFile)); } catch (FileNotFoundException e) { // ignore } } return ""; } /** * JFreeChart 图表转 base64 编码 */ public static String chartImage2Str(JFreeChart chart, int width, int height) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { ChartUtilities.writeChartAsJPEG(out, 1.0f, chart, width, height, null); out.flush(); byte[] data = out.toByteArray(); return image2Str(new ByteArrayInputStream(data)); } catch (IOException e) { // ignore } finally { try { out.close(); } catch (IOException e) { // ignore } } return ""; } /** * 网络图片转 base64 编码输出 */ public static String webImage2Str(String urlPath) { InputStream in = null; try { URL url = new URL(urlPath); URLConnection connection = url.openConnection(); connection.connect(); in = connection.getInputStream(); return image2Str(in); } catch (IOException e) { // ignore } finally { if (in != null) { try { in.close(); } catch (IOException e) { // ignore } } } return ""; } /** * 图片输入流转 base 64 编码 */ public static String image2Str(InputStream stream) { if (stream != null) { try { byte[] data = new byte[stream.available()]; int length = stream.read(data); if (length > 0) { return encoder.encode(data); } } catch (IOException e) { // ignore } } return ""; } }