JAVA生成EXCEL图表
跟据客户的要求,需要开发一套包括图形的报表,还需要导出WORD
图表需要这样:
这样:
这样:
还有这样:
接下来是实现思路:
以往用的最多的就是JFreechart,手上也有实现各种图形的资源,但是领导说用它做的图形太丑了, 所以没什么卵用。
FusionCharts到是漂亮,但是没有办法实现上述图表,客户也不能接受替代方案,同样没什么卵用
然后就是百度ECharts,漂亮、功能强大、灵活性强(JS/HTML5实现,可以随意更改源码), 但是有些功能不支待IE8, 还是没什么卵用
想了很多方法,最后又回到了原点,客户要生成Office, 就让Office给我们做,虽然Java对操作图表还没有什么好的方法,但是jacob是可以操作宏,我们通过宏实现所有图表功能。
1、 制作Excel图表模板
制作图表看起来简单,但是做起来相当的慢,好在客户提供一些类似图表,我做了一些调整就可以了 膜拜。。。。。
还有就是网上一堆的制作图表的教程,就不记录了。
2、 通过POI填充数据到Excel
1 /** 2 * 通过填充excel 生成图表 3 * @param list 图标数据 4 * @param inFile 模板文件 5 * @param outFile 输出文件 6 * @return 7 * @throws Exception 8 */ 9 public static void createChart(List<Map> list, String inFile,String outFile) { 10 if(list == null) list = new ArrayList(); 11 12 System.out.println("图表个数" + list.size()); 13 System.out.println("源文件路径" + inFile); 14 System.out.println("图表个数" + outFile); 15 16 try { 17 // 读取模板 18 FileInputStream is = new FileInputStream(inFile); 19 HSSFWorkbook wbs = new HSSFWorkbook(is); 20 21 int sheetIndex; 22 HSSFSheet sheet; //sheet 23 int rowIndex; 24 HSSFRow row; //行 25 int cellIndex; 26 HSSFCell cell; //列 27 String value; 28 float fvalue; 29 String valStr = "^[-+]?(([0-9]+)([.]([0-9]+))?|([.]([0-9]+))?)$"; 30 31 List<Integer> sheetList = new ArrayList(); 32 for(Map tempMap:list) { 33 // 读取工作表(data) 34 sheetIndex = Integer.parseInt(tempMap.get("SHEETINDEX") + ""); 35 sheet = wbs.getSheetAt(sheetIndex-1); 36 sheetList.add(sheetIndex); 37 38 /* 39 int i = 0; 40 List<HSSFPictureData> pictures = wbs.getAllPictures(); 41 for (HSSFPictureData picData : pictures) { 42 String ext = picData.suggestFileExtension(); 43 byte[] data = picData.getData(); 44 System.out.println(data.length); 45 46 47 //savePic(row, picData); 48 /* 49 String ext = picData.suggestFileExtension(); 50 51 byte[] data = picData.getData(); 52 if (ext.equals("jpeg")) { 53 FileOutputStream out = new FileOutputStream( 54 "D:\\Users\\Fancy1_Fan\\桌面\\work\\pict" + i + ".jpg"); 55 out.write(data); 56 out.close(); 57 } 58 if (ext.equals("png")) { 59 FileOutputStream out = new FileOutputStream( 60 "D:\\Users\\Fancy1_Fan\\桌面\\work\\pict" + i + ".png"); 61 out.write(data); 62 out.close(); 63 }*//* 64 i++; 65 }*/ 66 67 //添加行 68 rowIndex = Integer.parseInt(tempMap.get("ROWINDEX") + ""); 69 row = sheet.getRow(rowIndex-1); 70 if(row == null) row = sheet.createRow(rowIndex-1); 71 72 //列 73 cellIndex = Integer.parseInt(tempMap.get("CELLINDEX") + ""); 74 cell = row.getCell(cellIndex-1); 75 76 value = tempMap.get("DATAINFO") + ""; 77 if(value.matches(valStr)) { 78 fvalue = Float.parseFloat(value); 79 80 cell.setCellValue(fvalue); 81 } else { 82 cell.setCellValue(value); 83 } 84 //cell.setCellValue(tempMap.get("DATAINFO") + ""); //数据列 85 } 86 for(int temp:sheetList) { 87 sheet = wbs.getSheetAt(temp-1); 88 sheet.setForceFormulaRecalculation(true); 89 } 90 91 // 输出文件 92 FileOutputStream os = new FileOutputStream(outFile); 93 wbs.write(os); 94 is.close(); 95 os.close(); 96 } catch (Exception e) { 97 e.printStackTrace(); 98 } 99 }
3、 生成宏
我的基本思路是把所有生成的图片复至到一个指定Sheet, 然后Java代码只要到这个Sheet里获取图形
Sheets("QPLJYFJWXSQK").Select ActiveSheet.ChartObjects("图表 1").Activate ActiveChart.PlotArea.Select ActiveChart.ChartArea.Copy Sheets("CHART").Select Range("A41").Select ActiveSheet.Pictures.Paste.Select ActiveCell.FormulaR1C1 = "QPLJYFJWXSQK"
Sheets("QGJYFJLXSQK").Select Range("J13:S20").Select ' 复制位图 Selection.CopyPicture Appearance:=xlScreen, Format:=xlBitmap Sheets("CHART").Select Range("AA1").Select '粘贴为图片(问题:复制的位图自动添加边框线,通过裁减的方式删除边框线) ActiveSheet.Paste Selection.ShapeRange.PictureFormat.Crop.PictureOffsetX = -1 Selection.ShapeRange.PictureFormat.Crop.PictureOffsetY = -1 Selection.Copy Range("A1").Select ActiveSheet.Pictures.Paste.Select ActiveCell.FormulaR1C1 = "QGJYFJLXSQK"
只要会VBA 基本语法,其它的如果不会,可以通过录制宏得到
4、 调用宏
先提供工具类
1 package com.wiseda.fc.utils; 2 3 import com.jacob.activeX.ActiveXComponent; 4 import com.jacob.com.ComThread; 5 import com.jacob.com.Dispatch; 6 import com.jacob.com.Variant; 7 8 public class JacobExcelUtils { 9 private static ActiveXComponent xl = null; //Excel对象(防止打开多个) 10 private static Dispatch workbooks = null; //工作簿对象 11 private Dispatch workbook = null; //具体工作簿 12 private Dispatch sheets = null;// 获得sheets集合对象 13 private Dispatch currentSheet = null;// 当前sheet 14 15 public ActiveXComponent getXl() { 16 return xl; 17 } 18 19 public Dispatch getWorkbooks() { 20 return workbooks; 21 } 22 23 public Dispatch getWorkbook() { 24 return workbook; 25 } 26 27 /** 28 * 打开excel文件 29 * @param filepath 文件路径名称 30 * @param visible 是否显示打开 31 * @param readonly 是否只读方式打开 32 */ 33 public void OpenExcel(String filepath, boolean visible, boolean readonly) { 34 try { 35 initComponents(); //清空原始变量 36 ComThread.InitSTA(); 37 if(xl==null) 38 xl = new ActiveXComponent("Excel.Application"); //Excel对象 39 xl.setProperty("Visible", new Variant(visible));//设置是否显示打开excel 40 if(workbooks==null) 41 workbooks = xl.getProperty("Workbooks").toDispatch(); //工作簿对象 42 workbook = Dispatch.invoke( //打开具体工作簿 43 workbooks, 44 "Open", 45 Dispatch.Method, 46 new Object[] { filepath, new Variant(false), 47 new Variant(readonly) },// 是否以只读方式打开 48 new int[1]).toDispatch(); 49 } catch (Exception e) { 50 e.printStackTrace(); 51 releaseSource(); 52 } 53 } 54 55 /** 56 * 工作簿另存为 57 * @param filePath 另存为的路径 58 */ 59 public void SaveAs(String filePath){ 60 Dispatch.invoke(workbook, "SaveAs", Dispatch.Method, 61 new Object[] { filePath, 62 new Variant(44) }, new int[1]); 63 } 64 65 /** 66 * 关闭excel文档 67 * @param f 含义不明 (关闭是否保存?默认false) 68 */ 69 public void CloseExcel(boolean f,boolean quitXl) { 70 try { 71 Dispatch.call(workbook, "Save"); 72 Dispatch.call(workbook, "Close", new Variant(f)); 73 } catch (Exception e) { 74 e.printStackTrace(); 75 } finally { 76 if(quitXl){ 77 releaseSource(); 78 } 79 } 80 } 81 82 /** 83 * 释放资源 84 */ 85 public static void releaseSource(){ 86 if(xl!=null){ 87 xl.invoke("Quit", new Variant[] {}); 88 xl = null; 89 } 90 workbooks = null; 91 ComThread.Release(); 92 System.gc(); 93 } 94 95 /** 96 * 添加新的工作表(sheet),(添加后为默认为当前激活的工作表) 97 */ 98 public Dispatch addSheet() { 99 return Dispatch.get(Dispatch.get(workbook, "sheets").toDispatch(), "add").toDispatch(); 100 } 101 102 /** 103 * 修改当前工作表的名字 104 * @param newName 105 */ 106 public void modifyCurrentSheetName(String newName) { 107 Dispatch.put(getCurrentSheet(), "name", newName); 108 } 109 110 /** 111 * 得到当前工作表的名字 112 * @return 113 */ 114 public String getCurrentSheetName() { 115 return Dispatch.get(getCurrentSheet(), "name").toString(); 116 } 117 118 /** 119 * 得到工作薄的名字 120 * @return 121 */ 122 public String getWorkbookName() { 123 if(workbook==null) 124 return null; 125 return Dispatch.get(workbook, "name").toString(); 126 } 127 128 /** 129 * 得到sheets的集合对象 130 * @return 131 */ 132 public Dispatch getSheets() { 133 if(sheets==null) 134 sheets = Dispatch.get(workbook, "sheets").toDispatch(); 135 return sheets; 136 } 137 138 /** 139 * 得到当前sheet 140 * @return 141 */ 142 public Dispatch getCurrentSheet() { 143 currentSheet = Dispatch.get(workbook, "ActiveSheet").toDispatch(); 144 return currentSheet; 145 } 146 147 /** 148 * 通过工作表名字得到工作表 149 * @param name sheetName 150 * @return 151 */ 152 public Dispatch getSheetByName(String name) { 153 return Dispatch.invoke(getSheets(), "Item", Dispatch.Get, new Object[]{name}, new int[1]).toDispatch(); 154 } 155 156 /** 157 * 通过工作表索引得到工作表(第一个工作簿index为1) 158 * @param index 159 * @return sheet对象 160 */ 161 public Dispatch getSheetByIndex(Integer index) { 162 return Dispatch.invoke(getSheets(), "Item", Dispatch.Get, new Object[]{index}, new int[1]).toDispatch(); 163 } 164 165 /** 166 * 得到sheet的总数 167 * @return 168 */ 169 public int getSheetCount() { 170 int count = Dispatch.get(getSheets(), "count").toInt(); 171 return count; 172 } 173 174 /** 175 * 调用excel宏 176 * @param macroName 宏名 177 */ 178 public void callMacro(String macroName){ 179 Dispatch.call(xl, "Run",new Variant(macroName)); 180 } 181 182 /** 183 * 单元格写入值 184 * @param sheet 被操作的sheet 185 * @param position 单元格位置,如:C1 186 * @param type 值的属性 如:value 187 * @param value 188 */ 189 public void setValue(Dispatch sheet, String position, String type, Object value) { 190 191 Dispatch cell = Dispatch.invoke(sheet, "Range", 192 Dispatch.Get, new Object[] { position }, new int[1]) 193 .toDispatch(); 194 Dispatch.put(cell, type, value); 195 } 196 197 /** 198 * 单元格读取值 199 * @param position 单元格位置,如: C1 200 * @param sheet 201 * @return 202 */ 203 public Variant getValue(String position, Dispatch sheet) { 204 Dispatch cell = Dispatch.invoke(sheet, "Range", Dispatch.Get, 205 new Object[] { position }, new int[1]).toDispatch(); 206 Variant value = Dispatch.get(cell, "Value"); 207 return value; 208 } 209 210 private void initComponents(){ 211 workbook = null; 212 currentSheet = null; 213 sheets = null; 214 } 215 }
调用宏代码
1 public void excelPictureGrab() throws Exception { 2 this.targetFile = new File(targetFilePath); 3 4 //复制文件 5 FileUtils.copyFile(xlsFile, targetFile); 6 7 logger.info("文件复至完成"); 8 9 //生成带图形的 excel 10 JacobExcelUtils tool = new JacobExcelUtils(); 11 try { 12 tool.OpenExcel(targetFilePath, false, false); 13 14 logger.info("打开Excel"); 15 16 //执行宏 复制为图片 17 tool.callMacro("copyImg"); 18 19 logger.info("处理宏完成"); 20 } catch (Exception e) { 21 throw new Exception("执行宏出错," + e.getMessage()); 22 // TODO: handle exception 23 } finally { 24 // 关闭流 25 tool.CloseExcel(false, true); 26 } 27 }
5、 获取图表
1 //获取图片字节流 2 public static String getPic(String fileUrl,String backupUrl) throws Exception { 3 StringBuffer results = new StringBuffer(); 4 5 File tfdir = new File(backupUrl); 6 if (!tfdir.exists()) tfdir.mkdirs(); 7 8 InputStream inp = new FileInputStream(fileUrl); 9 HSSFWorkbook workbook = (HSSFWorkbook) WorkbookFactory.create(inp); 10 11 List<HSSFPictureData> pictures = workbook.getAllPictures(); 12 HSSFSheet sheet = (HSSFSheet) workbook.getSheetAt(0); //所有图片都存在第一页 13 14 int i = 0; 15 for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) { 16 HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); 17 18 if (shape instanceof HSSFPicture) { 19 HSSFPicture pic = (HSSFPicture) shape; 20 int pictureIndex = pic.getPictureIndex()-1; 21 22 //图片名称 23 int row = anchor.getRow1(); 24 25 System.out.println(anchor.getCol1()); 26 27 HSSFCell picCell = sheet.getRow(row).getCell(anchor.getCol1()); 28 29 String picName = ""; 30 if(picCell != null) { 31 picName = picCell.getStringCellValue(); 32 } 33 34 35 logger.info(i + "--->row." + anchor.getRow1() + ":cell." + anchor.getCol1() + ":pictureIndex." + pictureIndex); 36 37 HSSFPictureData picData = pictures.get(pictureIndex); 38 39 //备份并返回 图片 40 if(picName != null && !"".equals(picName)) { 41 String result = BackupPic(picName, picData,backupUrl); 42 results.append(result); 43 results.append(","); 44 } 45 } 46 i++; 47 } 48 49 return results.toString(); 50 } 51 52 //备份图片 53 private static String BackupPic(String picName,PictureData pic,String backupUrl) throws Exception { 54 String result = ""; 55 56 String pngImgUrl,jpgImgUrl; 57 String ext = pic.suggestFileExtension(); 58 byte[] data = pic.getData(); 59 if (ext.equals("png")) { 60 61 pngImgUrl = backupUrl + picName + "." + ext; 62 FileOutputStream out = new FileOutputStream( 63 pngImgUrl); 64 out.write(data); 65 out.close(); 66 67 //转为 jpg 68 jpgImgUrl = backupUrl + picName + ".jpg"; 69 ConvertPngToJpg(pngImgUrl,jpgImgUrl); 70 71 result = picName + ":" + DatatypeConverter.printBase64Binary(data); 72 } 73 74 return result; 75 } 76 77 //压缩图片 把PND图片转JPG 78 private static void ConvertPngToJpg(String pngImgUrl,String jpgImgUrl) { 79 BufferedImage bufferedImage; 80 try { 81 82 //read image file 83 bufferedImage = ImageIO.read(new File(pngImgUrl)); 84 85 // create a blank, RGB, same width and height, and a white background 86 BufferedImage newBufferedImage = new BufferedImage(bufferedImage.getWidth(), 87 bufferedImage.getHeight(), BufferedImage.TYPE_INT_RGB); 88 newBufferedImage.createGraphics().drawImage(bufferedImage, 0, 0, Color.WHITE, null); 89 90 // write to jpeg file 91 ImageIO.write(newBufferedImage, "jpg", new File(jpgImgUrl)); 92 93 } catch (IOException e) { 94 95 e.printStackTrace(); 96 97 } 98 }