课程建设网站设计源码,万能软文模板,企业网站下周,昆山建设招标信息网站项目中需要做一个统计报表功能#xff0c;实现各种Excel报表数据导出。要求表头能够动态配置#xff0c;表数据通过存储过程实现#xff0c;也要求能够动态配置。 技术选型#xff1a; 由于之前在项目中使用过easypoi#xff0c;相对于原生apache poi#xff0c;能够用很… 项目中需要做一个统计报表功能实现各种Excel报表数据导出。要求表头能够动态配置表数据通过存储过程实现也要求能够动态配置。 技术选型 由于之前在项目中使用过easypoi相对于原生apache poi能够用很少的代码写出Excel导入、导出功能且API清晰好理解。因此优先选择了使用easypoi验证功能需求能否实现。easypoi是基于apache poi开发在此基础上进行了封装和扩展特别复杂的功能就需要使用基础poi来开发了。
开发指南https://opensource.afterturn.cn/doc/easypoi.html
实现思路 由于配置的报表多是复杂多级表头而easypoi对于动态表头生成只支持两级简单来说就是表头最多两行所以这种方式就只能放弃。改选用配置动态模板的方式先做好模板然后配置到数据表里。
实现步骤 Maven pom中引入jar包
dependencygroupIdcn.afterturn/groupIdartifactIdeasypoi-base/artifactIdversion3.0.1/version
/dependency
dependencygroupIdcn.afterturn/groupIdartifactIdeasypoi-web/artifactIdversion3.0.1/version
/dependency
dependencygroupIdcn.afterturn/groupIdartifactIdeasypoi-annotation/artifactIdversion3.0.1/version
/dependencyReportController类 如下代码仅显示主要步骤 RequestMapping(/exportExcel.html)ResponseBodypublic void exportExcel(HttpServletResponse response, HttpSession session) {// 获取报表配置 ReportResultVo主要存储了 标题行数、模板路径位置、导出文件名称等ReportResultVo config reportService.getReportConfig(id);TemplateExportParams params new TemplateExportParams();// 标题开始行params.setHeadingStartRow(0);// 标题行数params.setHeadingRows(config.getHeadRowNum());// 设置sheetName若不设置该参数则使用得原本得sheet名称params.setSheetName(数据统计);// 获取报表内容 // 因为表数据是根据存储过程来实现的不同的报表有不同的配置// 所以使用MapString,Object格式来接收ListMapString, Object reportBodyList reportService.getReportBodyData(...);MapString, Object data new HashMapString, Object();data.put(list, reportBodyList);// 获取模板文件路径// 这里有个很坑的地方就是easypoi的API只能接收文件路径无法读取文件流String filePath 服务器上的某个路径或者项目中的某个路径// 设置模板路径params.setTemplateUrl(filePath);// 获取workbookWorkbook workbook ExcelExportUtil.exportExcel(params, data);// exportFileName代表导出的文件名称ReportUtils.export(response, workbook, exportFileName);ReportUtils类: // Excel 导出 通过浏览器下载的形式public static void export(HttpServletResponse response, Workbook workbook, String fileName) throws IOException {response.setHeader(Content-Disposition,attachment;filename new String(fileName.getBytes(UTF-8), iso8859-1));response.setContentType(application/vnd.ms-excel;charsetUTF-8);response.setHeader(Pragma, no-cache);response.setHeader(Cache-Control, no-cache);response.setDateHeader(Expires, 0);BufferedOutputStream bufferedOutPut new BufferedOutputStream(response.getOutputStream());workbook.write(bufferedOutPut);bufferedOutPut.flush();bufferedOutPut.close();}模板样式 模板以{{$fe:list 开头以}}结尾代表变遍历数据的意思每个字段前面的t.前缀是easypoi指定的默认值。
获取的报表内容字段名称要与模板里的字段一一对应 ListMapString, Object reportBodyList new ArrayList();MapString,Object values new HashMapStringObject();values.put(c1,总计)values.put(c2,10);values.put(c3,5);values.put(c4,8);values.put(c5,5);values.put(c6,8);values.put(c7,6);values.put(c8,3);reportBodyList.add(values);导出的Excel结果如下
到目前为止已经可以实现需求了但是实现的不够好尤其是上面提到的easypoi无法读取文件流只能从本地路径上获取文件模板极大的限制了程序的灵活性。而生产环境中的项目大多都会使用文件存储服务器比如fastdfs而不是把模板上传到web服务器上的某个路径下。 还有别的解决办法吗实在无法实现需求的话就只能使用apache poi了但是这种方式改动太大虽然可以灵活定制excel样式但是实现要复杂的多。思考良久后决定使用临时文件的方式解决这个问题。
实现思路 从fastdfs中获取文件流后写到本地临时目录然后让easypoi从本地临时目录里读取模板文件最后再删除临时文件。
关键代码如下 RequestMapping(/exportExcel.html)ResponseBodypublic void exportExcel(HttpServletResponse response, HttpSession session) {......try{// 从fastDfs上获取文件流 (fileStorage.readFile自己封装的API)InputStream inputStream fileStorage.readFile(filepath); // 模板临时目录String rootPath session.getServletContext().getRealPath(“template_temp/”);// 临时文件路径名String filePath rootPath _ new SimpleDateFormat(yyyyMMddHHmmss).format(new Date()) filename;tempFile new File(filePath);// 保存到临时文件ReportUtils.saveTempFile(inputStream, tempFile);// 设置模板路径params.setTemplateUrl(filePath);// 获取workbookWorkbook workbook ExcelExportUtil.exportExcel(params, data);// exportFileName代表导出的文件名称ReportUtils.export(response, workbook, exportFileName);} catch (Exception e) {throw new GeneralException(ErrorCode.REPORT_EXPORT_EXCEPTION);} finally {// 删除临时文件if (tempFile.exists()) {tempFile.delete();}}} ReportUtils类:
// 保存到临时目录
public static void saveTempFile(InputStream inputStream, File tempFile) throws IOException {if(!tempFile.getParentFile().exists()){ //如果文件的目录不存在tempFile.getParentFile().mkdirs(); //创建目录}OutputStream os new FileOutputStream(tempFile);byte[] b new byte[2048];int length;while ((length inputStream.read(b)) 0) {os.write(b, 0, length);}os.flush();os.close();inputStream.close();
}至此代码实现较好的满足了动态配置的需要如果大家有更好的方法欢迎提出 ------------本文结束感谢您的阅读------------