报表导出功能在JavaWeb 开发中非常常见,网上搜索相应的代码也非常多,大多都相似:利用POI生成excel 文件到服务器,再利用InputStream和Response 返回给前端做处理。无聊中发现WorkBook的write方法API如下:
随有更改stream为Response.getOutputStream()来减少创建文件的开销。实现工具类如下:
package com.newtouch.dssp.utils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.*;
/**
* @description: 基于POI的Excel工具类
* @author: [email protected]
* @date: 2019/8/15
**/
public class ExcelUtil {
private static final String XML ="xls";
private static final String XLSX ="xlsx";
/**
* @description: //todo 下载Excel (样式为通用样式,sheet页是一个)
* @author: [email protected]
* @param resp response返回
* @param fileName 文件名 不需要后缀 默认是xlsx
* @param datas 数据
* @param titles 表头
* */
public static void exportExcel(HttpServletResponse resp, String fileName,
Collection> datas, List titles)throws Exception{
resp.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
resp.addHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(fileName, "utf-8")+ ".xlsx");
exportExcel(resp.getOutputStream(),datas,titles);
}
/**
* @description: //todo Excel 写入OutputStream
* @author: [email protected]
* @param out 输出流
* @param datas 数据
* @param titles 表头
* */
public static void exportExcel(OutputStream out,Collection> datas, List titles){
XSSFWorkbook wb = new XSSFWorkbook();
try {
XSSFSheet sheet = wb.createSheet("new sheet");
writeDataToExcel(wb,sheet,datas,titles);
wb.write(out);
}catch (IOException ioe){
ioe.printStackTrace();
}
}
/**
* @description: // todo 读取上传的Excel文件
* @author: [email protected]
* @param file 上传的文件
* @param startRow 开始行 0开始
* @param startCell 开始列 0开始
* */
public static List readExcel(MultipartFile file, int startRow, int startCell)throws Exception{
checkFile(file);
String fileName = file.getOriginalFilename();
Workbook workbook = getWorkBook(file,fileName);
List list = new ArrayList<>();
if(workbook != null){
for(int sheetNum = 0;sheetNum < workbook.getNumberOfSheets();sheetNum++){
Sheet sheet = workbook.getSheetAt(sheetNum);
if(sheet == null){
continue;
}
int firstRowNum = sheet.getFirstRowNum();
int lastRowNum = sheet.getLastRowNum();
for(int rowNum = firstRowNum+startRow;rowNum <= lastRowNum;rowNum++){
Row row = sheet.getRow(rowNum);
if(row == null){
continue;
}
int firstCellNum = row.getFirstCellNum();
int lastCellNum = row.getLastCellNum()+1;
String[] cells = new String[lastCellNum];
for(int cellNum = firstCellNum + startCell; cellNum < lastCellNum;cellNum++){
Cell cell = row.getCell(cellNum);
cells[cellNum] = getCellValue(cell);
}
list.add(cells);
}
}
}
return list;
}
/**
* @description: //todo 写Excel
* @author: [email protected]
* @param workbook 工作区
* @param sheet sheet
* @param datas 数据
* @param titles 表头
* */
private static void writeDataToExcel(XSSFWorkbook workbook,Sheet sheet, Collection> datas, List titles){
int cellIndex =writeTitleToExcel(workbook, sheet, titles);
writeDataToExcel(workbook, sheet, datas);
autoSizeColumns(sheet,cellIndex);
}
/**
* @description: //todo 写标题到Excel
* @author: [email protected]
* @param workbook 工作区
* @param sheet sheet
* @param titles 表头
* */
private static int writeTitleToExcel(XSSFWorkbook workbook,Sheet sheet,List titles){
int colIndex=0;
Font titleFont = workbook.createFont();
titleFont.setFontName("simsun");
titleFont.setFontHeightInPoints((short) 14);
XSSFCellStyle titleStyle = workbook.createCellStyle();
titleStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER);
titleStyle.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);
titleStyle.setFont(titleFont);
Row titleRow = sheet.createRow(0);
titleRow.setHeightInPoints(25);
for (String title : titles) {
Cell cell = titleRow.createCell(colIndex);
cell.setCellValue(title);
cell.setCellStyle(titleStyle);
colIndex++;
}
return colIndex;
}
/**
* @description: //todo 写内容到Excel
* @author: [email protected]
* @param workbook 工作区
* @param sheet sheet
* @param datas 数据
* */
private static void writeDataToExcel(XSSFWorkbook workbook,Sheet sheet,Collection> datas){
int rowIndex = 1;
Font dataFont = workbook.createFont();
dataFont.setFontName("simsun");
dataFont.setColor(IndexedColors.BLACK.index);
XSSFCellStyle dataStyle = workbook.createCellStyle();
dataStyle.setAlignment(XSSFCellStyle.ALIGN_CENTER);
dataStyle.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);
dataStyle.setFont(dataFont);
Iterator> iterator = datas.iterator();
while (iterator.hasNext()){
Row dataRow = sheet.createRow(rowIndex);
Object data = iterator.next();
Class clazz = data.getClass();
Field [] fields = clazz.getDeclaredFields();
int cellIndex = 0;
for (Field field: fields){
Cell cell = dataRow.createCell(cellIndex);
cell.setCellStyle(dataStyle);
String fieldName = field.getName();
String methodName = "get"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
Method method;
Object value = null;
try {
method = clazz.getMethod(methodName,new Class[] {});
value = method.invoke(data,new Object[] {});
}catch (Exception e){
e.printStackTrace();
}
cell.setCellValue(value+"");
cellIndex ++;
}
rowIndex++;
}
}
/**
* @description: //todo 设置行宽度自适应 列不会设置
* @author: [email protected]
* @param sheet sheet
* @param columnNumber 结束列
* */
private static void autoSizeColumns(Sheet sheet, int columnNumber) {
for (int i = 0; i < columnNumber; i++) {
int oldWidth = sheet.getColumnWidth(i);
sheet.autoSizeColumn(i, true);
int newWidth = (int) (sheet.getColumnWidth(i) + 100);
if (newWidth > oldWidth) {
sheet.setColumnWidth(i, newWidth);
} else {
sheet.setColumnWidth(i, oldWidth);
}
}
}
/***
* //todo 校验文件是否正常
* @param file 上传的文件
* @throws Exception
*/
private static void checkFile(MultipartFile file)throws Exception{
if (Objects.isNull(file))
throw new FileNotFoundException("上传文件是空");
String fileName = file.getOriginalFilename();
if(!fileName.endsWith(XML) && !fileName.endsWith(XLSX))
throw new IOException(fileName + "不是excel文件");
}
/**
* @description: // todo 获取单元格的值
* @author: [email protected]
* @param cell 单元格
* @return String
*/
private static String getCellValue(Cell cell){
String cellValue = "";
if(cell == null){
return cellValue;
}
//把数字当成String来读,避免出现1读成1.0的情况
if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){
cell.setCellType(Cell.CELL_TYPE_STRING);
}
//判断数据的类型
switch (cell.getCellType()){
case Cell.CELL_TYPE_NUMERIC: //数字
cellValue = String.valueOf(cell.getNumericCellValue());
break;
case Cell.CELL_TYPE_STRING: //字符串
cellValue = String.valueOf(cell.getStringCellValue());
break;
case Cell.CELL_TYPE_BOOLEAN: //Boolean
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case Cell.CELL_TYPE_FORMULA: //公式
cellValue = String.valueOf(cell.getCellFormula());
break;
case Cell.CELL_TYPE_BLANK: //空值
cellValue = "";
break;
case Cell.CELL_TYPE_ERROR: //故障
cellValue = "非法字符";
break;
default:
cellValue = "未知类型";
break;
}
return cellValue;
}
/**
* @author: [email protected]
* @description: //todo 获取上传文件的工作区
* @param file 上传的文件
* @param fileName 文件名字
* @return Workbook
*/
private static Workbook getWorkBook(MultipartFile file,String fileName) {
Workbook workbook = null;
try {
InputStream is = file.getInputStream();
if(fileName.endsWith(XML)){
//2003
workbook = new HSSFWorkbook(is);
}else if(fileName.endsWith(XLSX)){
//2007
workbook = new XSSFWorkbook(is);
}
} catch (IOException e) {
e.printStackTrace();
}
return workbook;
}
/**
* @author: [email protected]
* @description: //todo 获取上传文件的工作区
* @param file 读取到的文件
* @return Workbook
*/
private static Workbook getWorkBook(File file) {
Workbook workbook = null;
try {
InputStream is = new FileInputStream(file);
if(file.getName().endsWith(XML)){
//2003
workbook = new HSSFWorkbook(is);
}else if(file.getName().endsWith(XLSX)){
//2007
workbook = new XSSFWorkbook(is);
}
} catch (IOException e) {
e.printStackTrace();
}
return workbook;
}
}
Controller 直接调exportExcel(HttpServletResponse resp, String fileName, Collection> datas, List
顺便贴上pom依赖版本:
org.apache.poi
poi
4.0.1
org.apache.poi
poi-ooxml
4.0.1
浏览器直接请求后台地址downUrl就可以下载 或者使用iframe
const down = {
excelDown(downUrl) {
var iframeOld = document.getElementsByTagName('iframe')
if (iframeOld && iframeOld.length > 0) {
iframeOld[0].remove()
}
var iframe = document.createElement('iframe')
iframe.src = downUrl
iframe.style.display = 'none'
document.body.appendChild(iframe)
}
}
export default down
2019-11-14 添加自动合并指定单元格行内容相同的方法
调用方式:
mergeRow(sheet,1,sheet.getLastRowNum(),0);
private void mergeRow(HSSFSheet sheet,int startRow,int endRow,int cellNo){
if (endRow
合并效果图:
后期业务系统说导出要加失败提醒,所以不能使用Ifram 做嵌套下载,况且当参数过多时候前端不好处理 ,所以在和前端沟通下,后端不变,使用新下载方式
export function downExcel(response) {
var blob = new Blob([response.data], {
type:
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'
})
var downloadElement = document.createElement('a')
var href = window.URL.createObjectURL(blob)
var contentDisposition = response.headers['content-disposition']
var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*')
var result = patt.exec(contentDisposition)
var filename = decodeURI(result[1])
downloadElement.style.display = 'none'
downloadElement.href = href
downloadElement.download = filename // ÏÂÔغóÎļþÃû
document.body.appendChild(downloadElement)
downloadElement.click() // µã»÷ÏÂÔØ
document.body.removeChild(downloadElement) // ÏÂÔØÍê³ÉÒƳýÔªËØ
window.URL.revokeObjectURL(href) // Êͷŵôblob¶ÔÏó
}