最近在工作中遇到的一个问题,要将html标签里的Table跟echarts生成报表导出到excel里面,没有搞过各种谷歌!-_-,还好解决了,就分享一下这次踩坑的经验了.
所使用的jar包:
commons-codec-1.9.jar
commons-lang3-3.3.2.jar
jsoup-1.8.3.jar
poi-3.9.jar
slf4j-api-1.7.7.jar
slf4j-simple-1.7.7.jar
table-to-xls-0.0.1-RELEASE.jar//这个可以在oscchina的git上找到的谢谢大神的代码了
首先说一说实现的思路:
前端用jquery获取Table标签的内容跟echarts报表的图片的base64----->2.ajax的post传输数据到后台----->3将获取到的数据分别处理,表格生成excel再将base64还原成图片插入到生成的excel里面
具体实现:
第一步:
首先说明一下echarts在生成图表之前都要先初始化一个东西所以base64可以根据这个来获取
var showCharts=[];//这是个全局变量用来接收每个chart对象的
//在每个chart初始化的时候将初始化的对象放进数组
var myChart = echarts.init(document.getElementById(tagid));
showCharts.push(myChart);
第二步:通过页面上的一个按钮触发一个函数(click事件)
function export2Excel(tableId){
var tableContext =$("#"+tableId).html();
var datas={}
if(tableContext.length>37){//这里为什么判断长度大于37因为表格动态生成的默认长度就有36!!:)
tableContext="<table>"+tableContext+"</table>";
datas.tableContext=tableContext;
}
if(showCharts.length!=0){
var imgUrls = [];
for(var x =0; x<showCharts.length;x++){
imgUrls.push(showCharts[x].getDataURL("png"));
}
datas.imgUrls=imgUrls;
}
if(datas.tableContext){
$.ajax({
url:url,//发送到后台处理这些数据的控制器地址(这个只是负责发送数据到后台)
type: "POST",
dataType: "json",
data: $.param(datas,true),//发送数组这样的数据格式必用这个东西不然很可能出问题
success: function (result) {
if(parseInt(result.status)==0){
location.href=url+fileName//用来处理下载请求的控制器地址跟下载的文件名,为啥要有这一步呢因为ajax不支持下载的浏览器是弹不出那个保存文件的框的(有没有草泥马)
}
},
failure: function (err) {
alert("ajax产生了错误!");
}
});
}else{
alert("必须有表格或者图表才能下载");
}
第三步:后台处理
//这个控制器是用来预处理ajax的post请求提交过来的数据的
@RequestMapping(value = "preExportToExcel",method=RequestMethod.POST)
@ResponseBody
public AjaxData<Map<String,String>> preExportToExcel(String tableContext, String[] imgUrls,HttpServletRequest req,HttpServletResponse rep) throws Exception {
AjaxData<Map<String,String>> ajax = new AjaxData<Map<String,String>>();
tableContext=HtmlUtils.htmlUnescape(tableContext);
String filePath=req.getSession().getServletContext().getRealPath("/")+"temp";
filePath = filePath.replace("\\","/")+"/";
HtmlTableToExcel html = new HtmlTableToExcel(imgUrls, filePath, tableContext);//这个对象会将传递过来的数据生成excel
String xlsFileName = html.mergePicAndExcel();
Map<String,String> map = new HashMap<String,String>();
map.put("xlsFileName", xlsFileName);
ajax.setData(map);
return ajax;
}
//这个控制器才是真正的下载控制器对应ajax里面的location.href那句代码
//采用servlet这种方式下载的原因就是可以在下载完成的时候可以将文件删掉
@RequestMapping(value = "exportToExcel",method=RequestMethod.GET)
@ResponseBody
public void exportToExcel(String xlsFileName, HttpServletResponse rep) throws Exception {
OutputStream out = null;
try {
rep.reset();
rep.setContentType("application/vnd.ms-excel");
rep.setHeader("Content-Disposition","attachment; filename=" + xlsFileName.substring(xlsFileName.lastIndexOf("/") + 1));
out = rep.getOutputStream();
System.out.println(xlsFileName);
out.write(FileUtils.readFileToByteArray(new File(xlsFileName)));
out.flush();
File file = new File(xlsFileName.substring(0,xlsFileName.lastIndexOf("/")));
File[] files= file.listFiles();
for (File file2 : files) {
FileUtils.deleteFile(file2.getAbsolutePath());
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage() + "文件下载失败");
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e.getMessage() + "文件下载失败");
}
}
}
}
所用类的代码:
值得注意的是图片的后缀名必须是.png结尾的额不然透明背景的图片插入到excel里面是要变成黑乎乎的
/**
* 将图片的base64码解密并还原图片(只支持支持格式"png","jpg",支持多张图片)
*
*/
public class Base64ToImage {
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(Base64ToImage.class);
/**
* 枚举类用来选择模式
*
*/
public static enum PictureType {
//png的图片类型
TYPE_PNG,TYPE_JPG;
}
/**
*
* @param imgUrls 多张图片的base64码组
* @param filePath 保存文件的路径
* @param type 生成文件的模式
* @return 返回生成的图片的名字
*/
public static List<String> base64ToPicture(String[] imgUrls,String filePath,Base64ToImage.PictureType type){
String suffix="";
//判断传入的图片类型
if(type==PictureType.TYPE_PNG) suffix=suffix+".png";
if(type==PictureType.TYPE_JPG) suffix=suffix+".jpg";
//判断空值以及0值
if(imgUrls.length==0||imgUrls==null||StringUtils.isBlank(filePath)){
return null;
}
List<String> fileNameList= new ArrayList<String>();
for (String imgUrl : imgUrls) {
String fileName = UUID.randomUUID().toString();
String realFileName=filePath+fileName;
if(isCreatePic(imgUrl, realFileName,suffix)) fileNameList.add(fileName+suffix);
logger.info(fileName);
}
return fileNameList;
}
/**
*
* @param imgsURl 图片的base64码
* @param fileName 图片名称
* @return 是否生成图片成功
*/
private static boolean isCreatePic(String imgsURl, String fileName,String suffix){
BASE64Decoder decoder = new BASE64Decoder();
boolean flag = true;
OutputStream out=null;
try {
String[] url = imgsURl.split(",");
String u = url[1];
// Base64解码
byte[] buffer = new BASE64Decoder().decodeBuffer(u);
// 生成图片
out= new FileOutputStream(new File(fileName + suffix));
out.write(buffer);
out.flush();
} catch (Exception e) {
flag = false;
}finally{
try {
if(out!=null)out.close();
} catch (IOException e) {
flag=false;
}
}
return flag;
}
}
// 将页面上的html 表格导出(支持页面上的报表图片导出,只支持单表的导出,只支持png格式图片插入)
public class HtmlTableToExcel {
private static final Logger logger = Logger.getLogger(HtmlTableToExcel.class);
//图片的base64码
private String[] imgUrls;
//存放生成文件的路径
private String filePath;
//生成的文件名字
// private String fileName=UUID.randomUUID().toString();
private String tableContext;
public HtmlTableToExcel(String[] imgUrls,String filePath, String tableContext) {
this.imgUrls = imgUrls;
this.tableContext = tableContext;
this.filePath=filePath;
}
public String createExcel(){
FileOutputStream fout=null;
try {
String xlsFileName =this.filePath+UUID.randomUUID().toString()+".xls";
fout = new FileOutputStream(xlsFileName);
TableToXls.process(tableContext, fout);
return xlsFileName;
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally{
try {
if(fout!=null)fout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public String mergePicAndExcel(){
String xlsFileName=createExcel();
if(imgUrls!=null){
List<String>fileNames = Base64ToImage.base64ToPicture(this.imgUrls, this.filePath, PictureType.TYPE_PNG);
for(int x = 0;x < fileNames.size();x++){
drawPiceToExcel(xlsFileName, this.filePath+fileNames.get(x),x);
}
}
return xlsFileName;
}
private void drawPiceToExcel(String xlsFileName,String picFileName,int position){
FileOutputStream fileOut = null;
BufferedImage bufferImg = null;
FileInputStream in = null;
Workbook wb = null;
try {
// 先把读进来的图片放到一个ByteArrayOutputStream中,以便产生ByteArray
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
bufferImg = ImageIO.read(new File(picFileName));
ImageIO.write(bufferImg, "png", byteArrayOut);
// 打开一个工作薄
in = new FileInputStream(xlsFileName);
POIFSFileSystem fs = new POIFSFileSystem(in);
wb = new HSSFWorkbook(fs);
HSSFSheet sheet = (HSSFSheet) wb.getSheetAt(0);
HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 1023, 100, (short) 0, sheet.getLastRowNum()+5*position+27*position,
(short) (sheet.getRow(0).getPhysicalNumberOfCells()+8), sheet.getLastRowNum()+5*position+27*(position+1));
// 插入图片
patriarch.createPicture(anchor,wb.addPicture(byteArrayOut.toByteArray(),HSSFWorkbook.PICTURE_TYPE_JPEG));
fileOut = new FileOutputStream(xlsFileName);
// 写入excel文件
wb.write(fileOut);
fileOut.close();
} catch (IOException io) {
throw new RuntimeException(io.getMessage()+"读取文件出问题");
} finally {
if (fileOut != null) {
try {
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}