package com.ywj.excel;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
public class Excel {
/**
*
* @Title: export
* @Description: (通用导出excel)
* @param excelName 文件名
* @param list 数据集合
* @param fieldMap 数据库字段和表头的值
* @param response
* @return void
*/
public static void export(String excelName,List<Map<String, Object>> list,LinkedHashMap<String, String> fieldMap,HttpServletResponse response){
// 设置默认文件名为当前时间:年月日时分秒
if (excelName==null || excelName=="") {
excelName = new SimpleDateFormat("yyyyMMddhhmmss").format(
new Date()).toString();
}
int total=list.size(); //总记录数
int excelBig=10000; //excel划分的大小
//判断需要分成多少个excel
int excelCount=(total%excelBig==0)?(total/excelBig):(total/excelBig+1);
//定义一个excel集合
List<HSSFWorkbook> excelList=new ArrayList<HSSFWorkbook>();
HSSFWorkbook wb=null; //excel工作表
int begin = 0; //数据源的开始位置
int end = 0; //数据源的结束位置
//循环得到excel集合
for(int i=0;i<excelCount;i++){
//第一次begin从零开始,否则从结束位置加1开始
if(end!=0){
begin=end;
}
end=begin+excelBig;
//判断如果最后的位置大于总记录数,则修改为总记录数
if(end>list.size()){
end=list.size();
}
//生成excel
wb=createExcel(excelName+(i+1), list,begin,end, fieldMap);
//将生成的excel添加到集合
excelList.add(wb);
}
//生成压缩文件,并导出
toZipOut(excelName, excelList, response);
}
/**
*
* @Title: toZipOut
* @Description: (压缩文件)
* @param excelName 文件命
* @param excelList wb工作簿集合
* @param response 将数据发送到页面上
* @return void
*/
private static void toZipOut(String excelName, List<HSSFWorkbook> excelList, HttpServletResponse response) {
try {
//将压缩包的名字默认为日期时间串
String fieldName=new SimpleDateFormat("yyyyMMddhhmmss").format(
new Date()).toString();
/**设置response头信息**/
response.reset();
//自己写状态码
response.setStatus(HttpServletResponse.SC_OK);
//使客户端浏览器,区分不同种类的数据,并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。
//ZIP与EXE文件的MIME类型同为application/octet-stream
response.setContentType( "application/octet-stream ");
//Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。
//Content-disposition其实可以控制用户请求所得的内容存为一个文件的时候提供一个默认的文件名,文件直接在浏览器上显示或者在访问时弹出文件下载对话框。
//此处设置下载文件的格式为.zip
response.setHeader("Content-Disposition","attachment;filename=\""+fieldName+".zip"+"\"");
//生成输出流
OutputStream ouputStream = response.getOutputStream();
//生成压缩包
ZipOutputStream zip = new ZipOutputStream(ouputStream);
// zip.setEncoding("GBK");//指定编码为gbk,否则部署到linux下会出现乱码
int i = 1;
String eName;
//循环文件列表,添加到压缩包中
for(HSSFWorkbook workbook:excelList){
//给每个文件名加序号
eName=excelName+String.valueOf(i);
//实例化一个压缩实体
ZipEntry entry = new ZipEntry(eName+".xls");
//将压缩实体放入压缩包
zip.putNextEntry(entry);
//将excel内容写进压缩实体
workbook.write(zip);
i++;
}
//将文件输出
zip.flush();
zip.close();
response.flushBuffer();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成Excel
* @param excelName 要导出的excel名称
* @param list 要导出的数据集合
* @param begin 数据集合的开始位置
* @param end 数据集合的结束位置
* @param fieldMap 中英文字段对应Map,即要导出的excel表头
* @return
*/
private static HSSFWorkbook createExcel(String excelName,List<Map<String, Object>> list,Integer begin,Integer end,LinkedHashMap<String, String> fieldMap){
int sheetContent=2000; //每个excel中sheet的容量
//计算sheet的数量
int sheetCount=0;
//如果总记录数 正好整除 以每个sheet的容量
if ((end-begin)%sheetContent==0){
//sheet的数量=总记录数 除以 每个sheet的容量
sheetCount=(end-begin)/sheetContent;
}else{
//sheet的数量=总记录数 除以 每个sheet的容量 加1
sheetCount=(end-begin)/sheetContent+1;
}
//创建一个WorkBook,对应一个Excel文件
HSSFWorkbook wb=new HSSFWorkbook();
//创建单元格,并设置值表头 设置表头居中
HSSFCellStyle style=wb.createCellStyle();
//创建一个居中格式
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
//定义sheet
HSSFSheet sheet=null;
//定义sheet的名称
String sheetName=excelName+"-";
//sheet中的数据开始位置
int beginSheet=0;
//sheet中的数据结束位置
int endSheet=0;
//循环创建sheet
for (int i=0;i<sheetCount;i++){
//在Workbook中,创建一个sheet,对应Excel中的工作薄(sheet)
sheet=wb.createSheet(sheetName+(i+1));
//第一次beginSheet从begin开始,否则从结束位置加1开始
if(endSheet==0){
beginSheet=begin;
}else{
beginSheet=endSheet;
}
//sheet结束位置=sheet开始位置+每个sheet的容量
endSheet=beginSheet+sheetContent;
//判断如果最后的位置大于总记录数,则修改为总记录数
if(endSheet>end){
endSheet=end;
}
// 填充工作表
try {
fillSheet(sheet,list,beginSheet,endSheet,fieldMap,style);
} catch (Exception e) {
e.printStackTrace();
}
}
//返回excel
return wb;
}
/**
*
* @Title: fillSheet
* @Description: (向每一个sheet页中填充row)
* @param sheet 页对象
* @param list 数据集合
* @param beginSheet 这一页开始的数据
* @param endSheet 这一夜结束的数据
* @param fieldMap 表头中文字段和数据库字段 键为数据库字段 值为中文字段
* @param style 表头样式
* @return void
*/
private static void fillSheet(HSSFSheet sheet, List<Map<String, Object>> list, int beginSheet, int endSheet,
LinkedHashMap<String, String> fieldMap, HSSFCellStyle style) {
// 定义存放英文字段名和中文字段名的数组
//英文为键中文为值
String[] enFields = new String[fieldMap.size()];
String[] cnFields = new String[fieldMap.size()];
// 将值分别填入两个数组
int count = 0;
for (Entry<String, String> entry : fieldMap.entrySet()) {
enFields[count] = entry.getKey();
cnFields[count] = entry.getValue();
count++;
}
int rowIndex=0;//行的数量
//在sheet中添加表头第0行
HSSFRow row=sheet.createRow(rowIndex++);
// 填充表头
for (int i = 0; i < cnFields.length; i++) {
HSSFCell cell=row.createCell(i);
cell.setCellValue(cnFields[i]);
cell.setCellStyle(style);
sheet.autoSizeColumn(20);
}
Map<String, Object> map=null;
for(int i=beginSheet;i<endSheet;i++) {
row=sheet.createRow(rowIndex++);
map= list.get(i);//map集合可以理解为对象
for (int j = 0; j < enFields.length; j++) {
String fieldValue=map.get(enFields[j]).toString();//用键取值
row.createCell(j).setCellValue(fieldValue);//将值赋给单元格
}
}
}
}