SpringBoot使用POI导出Excel文件,读取Exce内容的工具类封装和自动合并单元格(内容相同)。使用vue2 和axios 下载

报表导出功能在JavaWeb 开发中非常常见,网上搜索相应的代码也非常多,大多都相似:利用POI生成excel 文件到服务器,再利用InputStream和Response 返回给前端做处理。无聊中发现WorkBook的write方法API如下:

SpringBoot使用POI导出Excel文件,读取Exce内容的工具类封装和自动合并单元格(内容相同)。使用vue2 和axios 下载_第1张图片

随有更改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 titles)方法就可以下载,随后利用Swagger-ui做接口文档做测试,可以正常导出Excel,但无法打开文件,提示文件损坏。更改响应头也无济于事。随后利用浏览器直接发送请求可以正常下载使用文件,利用axios或者ajax 都是可以,原因不明。

顺便贴上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

合并效果图:

SpringBoot使用POI导出Excel文件,读取Exce内容的工具类封装和自动合并单元格(内容相同)。使用vue2 和axios 下载_第2张图片

 

后期业务系统说导出要加失败提醒,所以不能使用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¶ÔÏó
}

 

你可能感兴趣的:(工具类)