POI 導入、導出Excel


POI,全稱Apache POI,是Apache軟件基金會的開放源碼函式庫,POI提供API給Java程序對Microsoft Office格式檔案讀和寫的功能。項目地址:Apache POI - the Java API for Microsoft Documents

1 導入

使用form表單(enctype="multipart/form-data")上傳excel文件,后台接收MultipartFile文件格式。

讀取excel

    private static final String EXCEL_XLS = "xls";
    private static final String EXCEL_XLSX = "xlsx";

    /**
     * 判斷Excel的版本,獲取Workbook
     * @param in
     * @param file
     * @return
     * @throws IOException
     */
    public static Workbook getWorkbok(InputStream in, MultipartFile file) throws IOException {
        Workbook wb = null;
        if(file.getOriginalFilename().endsWith(EXCEL_XLS)){     //Excel 2003
            wb = new HSSFWorkbook(in);
        }else if(file.getOriginalFilename().endsWith(EXCEL_XLSX)){    // Excel 2007/2010
            wb = new XSSFWorkbook(in);
        }
        return wb;
    }

    /**
     * 判斷文件是否是excel
     * @throws Exception
     */
    public static void checkExcelVaild(MultipartFile file) throws Exception{
        if(ToolUtil.isEmpty(file)){
            throw new Exception("文件不存在");
        }
        if(!((file.getOriginalFilename().endsWith(EXCEL_XLS) || file.getOriginalFilename().endsWith(EXCEL_XLSX)))){
            throw new Exception("文件不是Excel");
        }
    }

    /**
     * 讀取Excel,兼容 Excel 2003/2007/2010
* @param excelFile
   * @param sheetIndex  從第幾個sheet開始遍歷
* @param dataRowIndex  從第幾行開始遍歷
*
@throws Exception */ public static List<Map<String, Object>> parseExcelObject(MultipartFile excelFile, int sheetIndex, int dataRowIndex) { List<Map<String,Object>> lists = new ArrayList<>(); SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); try { // 同時支持Excel 2003、2007 FileInputStream is = (FileInputStream) excelFile.getInputStream(); // 文件流 checkExcelVaild(excelFile); Workbook workbook = getWorkbok(is,excelFile); //Workbook workbook = WorkbookFactory.create(is); // 這種方式 Excel2003/2007/2010都是可以處理的 /** * 設置當前excel中sheet的下標:sheetIndex */ if(sheetIndex >= 0 && sheetIndex < workbook.getNumberOfSheets()) { for (; sheetIndex < workbook.getNumberOfSheets(); sheetIndex++) { int rowSize = 0; Sheet sheet = workbook.getSheetAt(sheetIndex); for(int rowIndex = dataRowIndex; rowIndex <= sheet.getLastRowNum(); rowIndex++) { Row row = sheet.getRow(rowIndex); if(row == null) continue; int tempRowSize = row.getLastCellNum(); if(tempRowSize > rowSize) rowSize = tempRowSize; Map<String, Object> maps = new HashMap<String, Object>(); String rowValue = ""; for(short columnIndex = 0; columnIndex < row.getLastCellNum(); columnIndex++) { Cell cell = row.getCell(columnIndex); if(ToolUtil.isEmpty(cell)) continue; int cellType = cell.getCellType(); String cellValue = ""; switch (cellType) { case Cell.CELL_TYPE_STRING: // 文本 cellValue = cell.getRichStringCellValue().getString().trim(); break; case Cell.CELL_TYPE_NUMERIC: // 數字、日期 if (DateUtil.isCellDateFormatted(cell)) { cellValue = fmt.format(cell.getDateCellValue()); } else { cell.setCellType(Cell.CELL_TYPE_STRING); cellValue = String.valueOf(cell.getRichStringCellValue().getString()); } break; case Cell.CELL_TYPE_BOOLEAN: // 布爾型 cellValue = String.valueOf(cell.getBooleanCellValue()); break; case Cell.CELL_TYPE_BLANK: // 空白 cellValue = cell.getStringCellValue(); break; case Cell.CELL_TYPE_ERROR: // 錯誤 cellValue = ""; break; case Cell.CELL_TYPE_FORMULA: // 公式 // 得到對應單元格的公式 //cellValue = cell.getCellFormula() + "#"; // 得到對應單元格的字符串 cell.setCellType(Cell.CELL_TYPE_STRING); cellValue = String.valueOf(cell.getRichStringCellValue().getString()); break; default: cellValue = ""; } // 保存數據 if (columnIndex == 0) { maps.put("name", cellValue); }
                 // ......其他邏輯...... } lists.add(maps); } } } is.close(); //關閉文件流 } catch (Exception e) { e.printStackTrace(); } finally{ } return lists; }

控制層

/**
 * 導入excel
 *
 * @param multipartFile
 * @return
 */
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
@ResponseBody
public void importExcel(@RequestParam("file") MultipartFile multipartFile) {
    if (multipartFile == null) {
          throw new Exception("excel文件請求參數錯誤");
    }
    List<Map<String, Object>> list = ExcelUtil.parseExcelObject(multipartFile, 0, 0); // 這里只解析第一個sheet,從第一行開始
    for(Map<String, Object> map : list){
        System.out.println(map.getString("name"));
    }
}

1 導出

如果你想看到下載彈框提示,如下圖,那么就需要使用form表單提交方式,請求后台接口。接下來會解釋為什么。

寫入excel

public static XSSFWorkbook createExcelObject(List<ExcelObject> list) {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        try {
            // 第一步,創建一個webbook文件,對應一個excel文件
            XSSFWorkbook wb = new XSSFWorkbook();
            // 第二部,在excel中添加一個sheet工作簿,參數為該工作簿名字,不寫為默認;
            XSSFSheet sheet = wb.createSheet("sheet1");
            // 第三部,做sheet中添加表頭第0行,注意老版本poi對excel的行數列數有限制short
            XSSFRow row = sheet.createRow((int) 0);

            // 第四部,設置單元格樣式
            XSSFCellStyle style = wb.createCellStyle();
            style.setAlignment(HorizontalAlignment.CENTER);//創建一個居中格式
            // 生成字體
            Font font = wb.createFont();
            font.setFontHeightInPoints((short) 12);
            font.setBold(true);
            // 把字體應用到當前的樣式
            style.setFont(font);

            // 第五部,設置好表頭內容
            XSSFCell cell = row.createCell(0);
            cell.setCelSPUue("name");
            cell.setCellStyle(style)

            // 第六部,寫入實體數據 實際應用中這些數據應該是從數據庫中得到
            for (int i = 0; i < list.size(); i++) {
                // 每次新建一行然后在新行中插入list中的數據對象,有點繁瑣,也許有更好的封裝方法,留待后看
                row = sheet.createRow((int) i + 1);
                row.createCell((int) 0).setCellValue(list.get(i).getName());
            }

            return wb;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
        return null;
    }    

  控制層

/**
 * 導出商品信息
 *
 * @param request
 * @param response
 * @throws Exception
 */
@RequestMapping(value = "/downExcel",method = RequestMethod.POST)
@ResponseBody
public void downExcel(HttpServletRequest request, HttpServletResponse response){

    List<ExcelObject> list; // 查詢出來的數據

    // 正確代碼順序
    //FileInputStream fs=new FileInputStream(excel);
    //XSSFWorkbook workbook = new XSSFWorkbook(fs);
    //FileOutputStream out=new FileOutputStream(excel);
    //workbook.write(out);

    XSSFWorkbook wb = ExcelUtil.createExcelObject(list);
    if (wb == null)
        throw new Exception("excel文件解析異常");

    try {
        // 設置輸出的格式
        response.reset();// 清空輸出流
        response.setHeader("Content-Disposition", "attachment; filename=" + new String(("excel.xlsx").getBytes(), "iso-8859-1"));// 設定輸出文件頭
        response.setContentType("application/x-download;charset=GBK");// 定義輸出類型

        //創建輸出流
        OutputStream outputStream = response.getOutputStream();

        wb.write(outputStream);
        outputStream.close();
    }catch (Exception e){
        e.printStackTrace();
    }
}

3 疑難解答

1、為什么一定要form表單請求,才會出現下載彈框?而ajax請求方式卻不會出現

因為,導出excel,在通過后台生成excel文件,並且以文件流的形式傳遞給前端,而ajax接收的返回數據類型只能是:字符串、xml。所以ajax處理不了返回的文件流。而瀏覽器可以處理。

  2、注意,導出時的代碼順序,否則報錯NotOfficeXmlFileException?

假如,你直接先把輸入流,輸出流建立好了以后,再創建新對象,就會報錯。錯誤信息為: org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException: No valid entries or contents found, this is not a valid OOXML (Office Open XML) file。

導出->控制層代碼,已經給出了正確的順序。

 

歡迎大家評論與交流,加油!!

 

【參考】

  1. https://blog.csdn.net/phil_jing/article/details/78307819
  2. https://www.cnblogs.com/xbq8080/p/7344258.html    ajax請求導出excel的問題
  3. https://blog.csdn.net/anlian523/article/details/72268347    XSSFWorkbook的順序問題

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM