赞
踩
Java通过POI或者一些常见的Excel工具类能够轻易导出后台的结构化数据,但是最近面临一个新需求,需要将对应记录数据和图片网络地址在同一行中导出。
一般我们看到的Excel表格是这样的。
首先,我们考虑需要使用List存放每一行的值,并且规定第一行即为表头,从第二行开始是数据。
伪代码是这样的:
List<List<Object>> rowList = new ArrayList<List<Object>>();
// Excel表头
rowList.add(Arrays.asList("序号","标题", "图片"));
for(int i=0;i<3;i++){
// 每一行的数据
rowList.add(Arrays.asList(i+1,"test"+(i+1),"imageUrl"+(i+1)));
}
// 调用生成Excel的方法
ExcelUtils.toImportExcel(rowList);
唯一需要注意的是,跟一般的导出Excel方法不同的是,我们第三列的图片是一个网络URL地址,我们需要将其转成png或者img格式并嵌入到对应的Excel单元格里,这就是比较特别的地方,下文中会着重介绍。
这里使用POI来解决问题,首先我们需要ExcelUtils工具类
public class ExcelUtils { /** * Excel导出设置Workbook * @param title 导出Excel文件名称 * @param rowList 第一个List为表头,其余行为表数据 * @param downLoadPic 是否下载图片 * @throws IOException */ public static HSSFWorkbook warpSingleWorkbook(String title,List<List<Object>> rowList, Boolean downLoadPic) throws IOException { if (rowList == null || rowList.isEmpty()) { throw new NullPointerException("the row list is null"); } HSSFWorkbook book = new HSSFWorkbook(); // 创建表 HSSFSheet sheet = book.createSheet(title); // 设置单元格默认宽度为20个字符 sheet.setDefaultColumnWidth(20); HSSFPatriarch patriarch = sheet.createDrawingPatriarch(); // 设置表头样式 HSSFCellStyle style = book.createCellStyle(); // 设置居左 style.setAlignment(HSSFCellStyle.ALIGN_LEFT); // 检测表头数据(表头不允许数据为空) List<Object> head = rowList.get(0); // 写数据 int size = rowList.get(0).size(); // 第几行 for (int i = 0; i < rowList.size(); i++) { List<Object> row = rowList.get(i); if (row == null || row.isEmpty()) { book.close(); throw new NullPointerException("the "+(i+1)+"th row is null"); } if (size != row.size()) { book.close(); throw new IllegalArgumentException("the cell number of "+(i+1)+"th row is different form the first"); } HSSFRow sr = sheet.createRow(i); // 第几列 for (int j = 0; j < row.size(); j++) { // 如果是否下载照片选择了true,由于我们的照片放在第三列,即当循环到序列为2的时候获取到网络图片地址,这里还考虑到了有多张照片的情况,且用逗号拼接成多张照片。 if (downLoadPic && i > 0 && j == 2) { if(StringUtils.isNotBlank(row.get(2).toString())){ sr.setHeight((short) (1800)); List<String> images = Arrays.asList(row.get(14).toString().split(",")); int temp = j; for(String image : images){ // 调用封装好的下载图片方法 drawPictureInfoExcel(book, patriarch, i, j, image); j++; } } } else { setExcelValue(sr.createCell(j), row.get(j), style); } } } return book; } /** * 设置Excel浮点数可做金额等数据统计 * @param cell 单元格类 * @param value 传入的值 */ public static void setExcelValue(HSSFCell cell, Object value, HSSFCellStyle style){ // 写数据 if (value == null) { cell.setCellValue(""); }else { if (value instanceof Integer || value instanceof Long) { cell.setCellType(Cell.CELL_TYPE_NUMERIC); cell.setCellValue(Long.valueOf(value.toString())); } else if (value instanceof BigDecimal) { cell.setCellType(Cell.CELL_TYPE_NUMERIC); cell.setCellValue(((BigDecimal)value).setScale(3, RoundingMode.HALF_UP).doubleValue()); } else { cell.setCellValue(value.toString()); } cell.setCellStyle(style); } } /** * * @param wb * @param patriarch * @param rowIndex * @param columnIndex * @param pictureUrl */ private static void drawPictureInfoExcel(HSSFWorkbook wb,HSSFPatriarch patriarch,int rowIndex, int columnIndex,String pictureUrl){ //rowIndex代表当前行 try { URL url = new URL(pictureUrl); //打开链接 HttpURLConnection conn = (HttpURLConnection)url.openConnection(); //设置请求方式为"GET" conn.setRequestMethod("GET"); //超时响应时间为5秒 conn.setConnectTimeout(5 * 1000); //通过输入流获取图片数据 InputStream inStream = conn.getInputStream(); //得到图片的二进制数据,以二进制封装得到数据,具有通用性 byte[] data = readInputStream(inStream); //anchor主要用于设置图片的属性 // dx1:起始单元格的x偏移量, // dy1:起始单元格的y偏移量, // dx2:终止单元格的x偏移量, // dy2:终止单元格的y偏移量, // col1:起始单元格列序号,从0开始计算; // row1:起始单元格行序号,从0开始计算, // col2:终止单元格列序号,从0开始计算; // row2:终止单元格行序号,从0开始计算, HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 250,(short) columnIndex, rowIndex, (short) columnIndex, rowIndex); //Sets the anchor type (图片在单元格的位置) //0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells. anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); patriarch.createPicture(anchor, wb.addPicture(data, HSSFWorkbook.PICTURE_TYPE_PNG)); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } private static byte[] readInputStream(InputStream inStream) throws Exception{ ByteArrayOutputStream outStream = new ByteArrayOutputStream(); //创建一个Buffer字符串 byte[] buffer = new byte[1024]; //每次读取的字符串长度,如果为-1,代表全部读取完毕 int len = 0; //使用一个输入流从buffer里把数据读取出来 while( (len=inStream.read(buffer)) != -1 ){ //用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度 outStream.write(buffer, 0, len); } //关闭输入流 inStream.close(); //把outStream里的数据写入内存 return outStream.toByteArray(); } }
调用规则,从主入口进入方法,这里我们用下载蜡笔小新图片到Excel里为例,找到一张网络URL图片地址为:https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595325136781&di=2f03af92dfac5476b81ad22f426e76ad&imgtype=0&src=http%3A%2F%2Fcdn.duitang.com%2Fuploads%2Fitem%2F201406%2F15%2F20140615002128_eWXnf.jpeg
@ApiOperation(value = "带图片报表导出", notes = "带图片报表导出", httpMethod = "GET") @RequestMapping(value = "/importReport", method = RequestMethod.GET) public void importReport(HttpServletRequest req, HttpServletResponse res) { List<List<Object>> rowList = new ArrayList<List<Object>>(); // Excel表头 rowList.add(Arrays.asList("序号","标题", "图片")); for(int i=0;i<3;i++){ // 下载三张蜡笔小新图片到Excel中 rowList.add(Arrays.asList(i+1,"test"+(i+1),"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595325136781&di=2f03af92dfac5476b81ad22f426e76ad&imgtype=0&src=http%3A%2F%2Fcdn.duitang.com%2Fuploads%2Fitem%2F201406%2F15%2F20140615002128_eWXnf.jpeg")); } // 接下来需要调用生成Excel的方法,唯一需要注意的是第三列的图片是一个网络URL地址,我们需要将其转成png或者img格式并嵌入到对应的Excel单元格里 try { HSSFWorkbook wb = ExcelUtils.warpSingleWorkbook("带图片报表", rowList, true); res.reset(); res.setHeader("Content-type","application/octet-stream"); res.setContentType("application/octet-stream"); res.setContentType("application/vnd.ms-excel"); res.setHeader("Accept-Ranges", "bytes"); String fileName = "带图片报表" + "_" + CalendarUtils.doFormatString(new Date(), CalendarUtils.YYYYMMDDHHmmss) + ".xls"; fileName = new String(fileName.getBytes(), CommConstants.CHAESET_ZH); res.setHeader("Content-Disposition", "attachment;filename=" + fileName); // 文件流输出到rs里 wb.write(res.getOutputStream()); res.getOutputStream().flush(); res.getOutputStream().close(); wb.close(); } catch (IOException e) { e.printStackTrace(); } }
在网页端调用该方法后,导出的报表结果如下所示:
唯一需要注意的是导出网络图片到Excel需要解析网络图片,且需要设置导出图片的单元格位置,在理清楚需求之后网上有许多已经造好的轮子可以复用。而时间充裕的话,建议大家可以去研究一下文中ExcelUtils工具类中的源码。
参考资料:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。