easyPOI基本用法


參考網址:http://www.wupaas.com/

1.Excel文件的簡單導入和導出

項目源碼:https://github.com/zhongyushi-git/springboot-easypoi.git。后台在easypoi-demo-admin目錄下,前端在easypoi-demo目錄下。

!!!說明:源碼中可能與下面的介紹的代碼稍有差異,請以源碼為准。

1.1准備工作

1)首先新建一個SpringBoot的項目,搭建基本的環境訪問數據,詳見源碼。

2)導入easypoi依賴

定義版本

<easypoi.version>4.1.0</easypoi.version>

坐標:這里是以springmvc的坐標導入的,適用大部分功能。如果需求不多,可以直接導入springboot對應的坐標,二者選一。選擇依據就是如果報錯,就換另一種坐標即可。

 <!--easypoi-->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>${easypoi.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>${easypoi.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>${easypoi.version}</version>
        </dependency>

springboot的坐標

<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-spring-boot-starter</artifactId>
    <version>3.3.0</version>
</dependency>

3)創建Excel操作的工具類ExcelUtils

package com.example.easypoidemoadmin.utils;

import cn.afterturn.easypoi.cache.manager.POICacheManager;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.ExcelXorHtmlUtil;
import cn.afterturn.easypoi.excel.entity.ExcelToHtmlParams;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.word.WordExportUtil;
import cn.afterturn.easypoi.word.parse.ParseWord07;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * Excel導入導出工具類
 */

public class ExcelUtils {

    /**
     * excel 導出
     *
     * @param list     數據列表
     * @param fileName 導出時的excel名稱
     * @param response
     */
    public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
        defaultExport(list, fileName, response);
    }

    /**
     * 默認的 excel 導出
     *
     * @param list     數據列表
     * @param fileName 導出時的excel名稱
     * @param response
     */
    private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
        //把數據添加到excel表格中
        Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * excel 導出
     *
     * @param list         數據列表
     * @param pojoClass    pojo類型
     * @param fileName     導出時的excel名稱
     * @param response
     * @param exportParams 導出參數(標題、sheet名稱、是否創建表頭,表格類型)
     */
    private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
        //把數據添加到excel表格中
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * excel 導出
     *
     * @param list         數據列表
     * @param pojoClass    pojo類型
     * @param fileName     導出時的excel名稱
     * @param exportParams 導出參數(標題、sheet名稱、是否創建表頭,表格類型)
     * @param response
     */
    public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, ExportParams exportParams, HttpServletResponse response) throws IOException {
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    /**
     * excel 導出
     *
     * @param list      數據列表
     * @param title     表格內數據標題
     * @param sheetName sheet名稱
     * @param pojoClass pojo類型
     * @param fileName  導出時的excel名稱
     * @param response
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) throws IOException {
        defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF));
    }



    /**
     * excel 導出
     *
     * @param list           數據列表
     * @param title          表格內數據標題
     * @param sheetName      sheet名稱
     * @param pojoClass      pojo類型
     * @param fileName       導出時的excel名稱
     * @param isCreateHeader 是否創建表頭
     * @param response
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) throws IOException {
        ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
        exportParams.setCreateHeadRows(isCreateHeader);
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }


    /**
     * excel下載
     *
     * @param fileName 下載時的文件名稱
     * @param response
     * @param workbook excel數據
     */
    private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }



    /**
     * excel 導入
     *
     * @param file      excel文件
     * @param pojoClass pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Class<T> pojoClass) throws IOException {
        return importExcel(file, 1, 1, pojoClass);
    }

    /**
     * excel 導入
     *
     * @param filePath   excel文件路徑
     * @param titleRows  表格內數據標題行
     * @param headerRows 表頭行
     * @param pojoClass  pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (StringUtils.isBlank(filePath)) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setNeedSave(true);
        params.setSaveUrl("/excel/");
        try {
            return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("模板不能為空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }


    /**
     * excel 導入
     *
     * @param file       上傳的文件
     * @param titleRows  表格內數據標題行
     * @param headerRows 表頭行
     * @param pojoClass  pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (file == null) {
            return null;
        }
        try {
            return importExcel(file.getInputStream(), titleRows, headerRows, pojoClass);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 導入
     *
     * @param inputStream 文件輸入流
     * @param titleRows   表格內數據標題行
     * @param headerRows  表頭行
     * @param pojoClass   pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (inputStream == null) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setSaveUrl("/excel/");
        params.setNeedSave(true);
        try {
            return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("excel文件不能為空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }
}

4)創建數據庫db2020及表user,執行腳本在根目錄下。

5)excel表格要導入的數據文件在項目根路徑的template文件夾下

6)使用vue-cli新建一個vue的項目,並安裝需要的插件。項目對axios進行了封裝,調用的時候,直接在js中使用即可,詳見源碼。

7)最后一點,要配置文件中加一行配置

#easypoi啟用覆蓋
spring
  main:
    allow-bean-definition-overriding: true

1.2導入

excel文件的導入,主要就是把文件上傳之后把內容讀取出來進行相應的操作。

1)編寫controller導入接口,service及dao詳見源碼。

 /**
     * 導入數據
     * @param file
     * @return
     * @throws IOException
     */
    @RequestMapping(value = "/import", method = RequestMethod.POST)
    public CommonResult importExcel(@RequestParam("file") MultipartFile file) throws IOException {
        List<User> list = ExcelUtils.importExcel(file, User.class);
        int i = userService.insertByBatch(list);
        if (i != 0) {
            return new CommonResult(200, "導入成功");
        } else {
            return new CommonResult(444, "導入失敗");
        }
    }

2)新建User實體類,給屬性添加@Excel注解

package com.example.easypoidemoadmin.entity;

import cn.afterturn.easypoi.excel.annotation.Excel;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author zhongyushi
 * @date 2020/6/24 0024
 * @dec 用戶實體
 */
@Data
@TableName(value = "User")
public class User {

    /**
     * 用戶名
     */
    @TableId(value = "username")
    @Excel(name = "用戶名",)
    private String username;

    /**
     * 姓名
     */
    @TableField(value = "name")
    @Excel(name = "姓名")
    private String name;

    /**
     * 年齡
     */
    @TableField(value = "age")
    @Excel(name = "年齡")
    private Integer age;

    /**
     * 性別,0表示男,1表示女
     */
    @TableField(value = "sex")
    @Excel(name = "性別",replace = {"男_0", "女_1"})
    private String sex;

    /**
     * 籍貫
     */
    @TableField(value = "address")
    @Excel(name = "籍貫")
    private String address;
}

需要注意的是,上述的導入的excel內容必須包含表頭和標題,否則讀取不到內容。在性別這里,分別使用數字代替文字,存儲方便。

3)頁面導入的組件

  <el-upload class="upload-demo" action="" :limit="1" :http-request="importExcel" :show-file-list="false" :file-list="fileList">
            <el-button size="small" type="primary" icon="el-icon-upload">導入</el-button>
          </el-upload>

4)頁面導入的方法

  //導入
      importExcel(param) {
        const formData = new FormData()
        formData.append('file', param.file)
        home.upload(formData).then(res => {
          if (res.code == 200) {
            this.fileList = []
            this.$message.success("導入成功")
            this.getList()
          } else {
            this.$message.error("導入失敗")
          }
        }).catch(err =>{
          console.log(err)
          this.$message.error("導入失敗")
        })
      } 

導入的模板在后台代碼的項目根目錄下的template目錄下。

5)注意事項

A:excel表格的表頭必須和@Excel的name屬性一樣,否則讀取不到數據。

B:若導入的字段包含日期類型,那么需要指定導入時的日期的格式並標明是必導入字段,如下所示,excel的內容的日期也需要是這種格式

@Excel(name = "日期",isImportField = "true", importFormat =  "yyyy-MM-dd" ,databaseFormat = "yyyy-MM-dd")

C:若導出的字段包含日期類型,那么需要指定導出的格式

@Excel(name = "日期",exportFormat = "yyyy-MM-dd", databaseFormat = "yyyy-MM-dd")

二者綜合的代碼如下,下一小節的導出日期就不再說明。

@Excel(name = "日期",isImportField = "true",exportFormat = "yyyy-MM-dd", importFormat =  "yyyy-MM-dd" ,databaseFormat = "yyyy-MM-dd")

1.3導出

導入就是根據查詢的條件把查詢結果先寫到excel表格中,然后下載這個excel即可。

1)編寫controller導出接口,service及dao詳見源碼。

/**
     * 導出數據,使用map接收
     *
     * @param map
     * @param response
     * @throws IOException
     */
    @PostMapping("/exportExcel")
    public void exportExcel(@RequestBody Map<String, Object> map, HttpServletResponse response) throws IOException {
        IPage<User> iPage = userService.getList((String) map.get("name"), (Integer) map.get("page"), (Integer) map.get("limit"));
        ExcelUtils.exportExcel(iPage.getRecords(), (String) map.get("title"), (String) map.get("sheetName"), User.class, (String) map.get("fileName"), response);
    }

2)給實體類@Excel注解添加其他屬性

package com.example.easypoidemoadmin.entity;

import cn.afterturn.easypoi.excel.annotation.Excel;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author zhongyushi
 * @date 2020/6/24 0024
 * @dec 用戶實體
 */
@Data
@TableName(value = "User")
public class User {

    /**
     * 用戶名
     */
    @TableId(value = "username")
    @Excel(name = "用戶名", orderNum = "0", width = 30)
    private String username;

    /**
     * 姓名
     */
    @TableField(value = "name")
    @Excel(name = "姓名", orderNum = "1", width = 30)
    private String name;

    /**
     * 年齡
     */
    @TableField(value = "age")
    @Excel(name = "年齡", orderNum = "2", width = 30)
    private Integer age;

    /**
     * 性別,0表示男,1表示女
     */
    @TableField(value = "sex")
    @Excel(name = "性別", orderNum = "3", width = 30,replace = {"男_0", "女_1"})
    private String sex;

    /**
     * 籍貫
     */
    @TableField(value = "address")
    @Excel(name = "籍貫", orderNum = "4", width = 30)
    private String address;
}

3)頁面導出的方法

 //導出
      exportExcel() {
        this.downloadLoading = true
        home.exportExcel({
          title: '用戶基本信息',
          sheetName: '用戶信息',
          fileName: '用戶信息表',
          name: this.pageData.name,
          page: this.pageData.page,
          limit: this.pageData.limit,
        }).then(res => {
          //使用js下載文件
          fileDownload(res, '用戶信息表.xlsx')
        }).finally(() => {
          this.downloadLoading = false;
        });
      },

這里使用到了js-file-download插件,它是用來幫助下載文件的。當下載文件時,很多時候都是在地址欄輸入url后瀏覽器自動幫忙下載,但是要統一請求方式,就把返回的二進制文件交給js-file-download進行處理后再下載。需要注意的是,這個導出的請求,我封裝了一個單獨的方法,需要指定響應的方式,否則無法下載后的文件是空的,方法截圖如下:

1.4圖片的導出

有了上面的導出基礎,圖片的導出就很簡單了。

1)新建一個實體類,用於和上面的實體類區分

package com.example.easypoidemoadmin.entity;

import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;

/**
 * @author zhongyushi
 * @date 2020/6/26 0026
 * @dec 描述
 */
@Data
public class Company {
    @Excel(name = "公司名稱",width =20)
    private String name;

    /**
     * type為 2 表示字段類型為圖片
     * imageType為 1 表示從file讀取
     */
    @Excel(name = "公司logo",width =20,type = 2,imageType = 1)
    private String logo;

    @Excel(name = "公司介紹",width =100)
    private String dec;

    public Company(String name,String logo,String dec){
        this.name=name;
        this.logo=logo;
        this.dec=dec;
    }
}

2)創建接口,圖片請自行下載。

/**
     * 圖片的導出
     *
     * @param response
     * @throws IOException
     */
    @PostMapping("/imgexport")
    public void imgExport(HttpServletResponse response,@RequestBody Map<String, Object> map) throws IOException {
        List<Company> list = new ArrayList<>();
        //圖片的路徑自定義,但必須要正確
        list.add(new Company("百度", "E:/img/1.jpg", "百度一下你就知道"));
        list.add(new Company("騰訊", "E:/img/3.jpg", "騰訊qq,交流的世界"));
        list.add(new Company("阿里巴巴", "E:/img/2.jpg", "阿里巴巴,馬雲的驕傲"));
        String fileName = map.get("fileName").toString();
        ExcelUtils.exportExcel(list, fileName, fileName, Company.class, fileName, response);
    }

3)在頁面添加導出的按鈕,點擊按鈕即可進行下載,下載的文件如圖

1.5圖片的導入

1)給Company對象加上無參構造,否則會出現異常

  public Company(){}

2)導入接口

 /**
     * 導入圖片
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("/imgimport")
    public CommonResult imgImport(@RequestParam("file") MultipartFile file) throws IOException {
        List<Company> list = ExcelUtils.importExcel(file, Company.class);
       return new CommonResult(200,"導入成功",list);
    }

3)參考excel的導入,添加一個導入的按鈕和請求的方法,詳見源碼

4)點擊excel圖片上傳,把上一步導出的文件進行導入,看到瀏覽器返回的數據如圖

1.6excel模板導出文件

也可以使用固定的模板來導出excel。

1)在工具類添加方法

    /**
     * 根據模板生成excel后導出
     * @param templatePath  模板路徑
     * @param map 數據集合
     * @param fileName 文件名
     * @param response
     * @throws IOException
     */
    public static void exportExcel(TemplateExportParams templatePath, Map<String, Object> map,String fileName, HttpServletResponse response) throws IOException {
        Workbook workbook = ExcelExportUtil.exportExcel(templatePath, map);
        downLoadExcel(fileName, response, workbook);
    }

2)編寫模板excel。截圖如下,模板文件在項目根路徑的template文件夾下:

在兩個大括號里寫對應的數據名稱。$fe用來遍歷數據,fe的寫法 fe標志 : list數據 單個元素數據(默認t,不需要寫) {{$fe: maplist t.id }}

3)接口

/**
     * 使用模板excel導出
     *
     * @param response
     * @throws Exception
     */
    @PostMapping("/excelTemplate")
    public void makeExcelTemplate(HttpServletResponse response, @RequestBody Map<String, Object> param) throws Exception {
        TemplateExportParams templatePath = new TemplateExportParams("E:/excel/用戶信息文件模板.xls");
        Map<String, Object> map = new HashMap<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        map.put("date", sdf.format(new Date()));
        map.put("user", "admin");
        IPage<User> ipages = userService.getList("", 1, 10);
        map.put("userList", ipages.getRecords());
        ExcelUtils.exportExcel(templatePath, map, param.get("fileName").toString(), response);
    }

在接口中,指定模板文件的路徑,然后給定數據,map的key值要和模板的值保持一致。

4)頁面添加按鈕和請求方法,見源碼。點擊即可下載。

1.7excel轉html

1)在工具類添加方法

    /**
     * excel轉html預覽
     * @param filePath 文件路徑
     * @param response
     * @throws Exception
     */
    public static void excelToHtml(String filePath,HttpServletResponse response) throws Exception{
        ExcelToHtmlParams params = new ExcelToHtmlParams(WorkbookFactory.create(POICacheManager.getFile(filePath)),true);
        response.getOutputStream().write(ExcelXorHtmlUtil.excelToHtml(params).getBytes());
    }

2)編寫接口

/**
     * EXCEL轉html預覽
     */
    @GetMapping("previewExcel")
    public void excelToHtml(HttpServletResponse response) throws Exception {
        ExcelUtils.excelToHtml("E:/excel/用戶信息導入模板.xlsx",response);
    }

3)頁面添加按鈕和請求方法,見源碼。點擊即可在彈框中顯示。

2.Word文件導出

2.1使用word模板導出

1)導入easypoi-base的依賴

        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>${easypoi.version}</version>
        </dependency>

2)在工具類加兩個方法

/**
     * word下載
     *
     * @param fileName 下載時的文件名稱
     * @param response
     * @param doc
     */
    private static void downLoadWord(String fileName, HttpServletResponse response, XWPFDocument doc) throws IOException {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/msword");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".docx" , "UTF-8"));
            doc.write(response.getOutputStream());
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }
   /**
     * word模板導出
     * @param map
     * @param templatePath
     * @param fileName
     * @param response
     * @throws Exception
     */
    public static void WordTemplateExport(Map<String, Object> map,String templatePath,String fileName,HttpServletResponse response) throws Exception {
        XWPFDocument doc = WordExportUtil.exportWord07(templatePath, map);
        downLoadWord(fileName,response,doc);
    }

3)接口,模板文件在項目根路徑的template文件夾下,圖片自定義下載(注意:如果要設置圖片,必須把導入的jar的版本改為3.3.0,否則會報錯,原因是新版本沒有這個實體類):

    /**
     * 使用模板word導出數據
     * @param param
     * @param response
     */
    @PostMapping("/wordTemplate")
    public void makeWordTemplate(@RequestBody Map<String, Object> param,HttpServletResponse response) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", "張三");
        map.put("nativePlace", "湖北武漢");
        map.put("age", "20");
        map.put("nation", "漢族");
        map.put("phone", "15685654524");
        map.put("experience", "湖北武漢,工作三年,java工程師");
        map.put("evaluate", "優秀,善良,老實");
        //設置圖片,如果無圖片,不設置即可
        WordImageEntity image = new WordImageEntity();
        image.setHeight(200);
        image.setWidth(150);
        image.setUrl("E:/excel/pic.jpg");
        image.setType(WordImageEntity.URL);
        map.put("picture", image);
        try {
           ExcelUtils.WordTemplateExport(map,"E:/excel/個人簡歷模板.docx",param.get("fileName").toString(),response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

4)頁面添加按鈕和請求方法,見源碼。點擊即可下載。上面案例導出時有圖片,如果不需要圖片,可不設置圖片路徑即可。

2.2使用word模板導出多頁

單模板生成多頁數據在合適的場景也是需要的,比如一個訂單詳情信息模板,但是有很多訂單,需要導入到一個word里面。

1)在工具類添加方法

    /**
     * word模板導出多頁
     * @param list
     * @param templatePath
     * @param fileName
     * @param response
     * @throws Exception
     */
    public static void WordTemplateExportMorePage(List<Map<String, Object>> list, String templatePath, String fileName, HttpServletResponse response) throws Exception {
        XWPFDocument doc = new ParseWord07().parseWord(templatePath, list);
        downLoadWord(fileName, response, doc);
    }

2)接口

 /**
     * word模板導出多頁
     * @param param
     * @param response
     */
    @PostMapping("/wordTemplateMorePage")
    public void makeWordTemplateMorePage(@RequestBody Map<String, Object> param, HttpServletResponse response) {
        List<Map<String, Object>> list=new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Map<String, Object> person = new HashMap<>();
            person.put("name", "張三"+i);
            person.put("nativePlace", "湖北武漢"+i);
            person.put("age", 20+i);
            person.put("nation", "漢族");
            person.put("phone", "15685654524");
            person.put("experience", "湖北武漢,工作三年,java工程師");
            person.put("evaluate", "優秀,善良,老實");
            person.put("picture", "");
            list.add(person);
        }
        try {
            ExcelUtils.WordTemplateExportMorePage(list, "E:/excel/個人簡歷模板.docx", param.get("fileName").toString(), response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3)頁面添加按鈕和請求方法,見源碼。點擊即可下載。

3.excel導入時驗證

有時候需要在導入時先驗證數據的合法性再進行導出,為了演示的完整性,需要使用新的頁面進行導入操作。步驟如下:

3.1環境准備

1)新建表student

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年齡',
  `birth` date DEFAULT NULL COMMENT '出生日期',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

2)在ExcelUtils工具類添加方法(標紅)

package com.example.easypoidemoadmin.utils;

import cn.afterturn.easypoi.cache.manager.POICacheManager;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.ExcelXorHtmlUtil;
import cn.afterturn.easypoi.excel.entity.ExcelToHtmlParams;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.word.WordExportUtil;
import cn.afterturn.easypoi.word.parse.ParseWord07;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * Excel導入導出工具類
 */

public class ExcelUtils {

    /**
     * excel 導出
     *
     * @param list     數據列表
     * @param fileName 導出時的excel名稱
     * @param response
     */
    public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
        defaultExport(list, fileName, response);
    }

    /**
     * 默認的 excel 導出
     *
     * @param list     數據列表
     * @param fileName 導出時的excel名稱
     * @param response
     */
    private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response) throws IOException {
        //把數據添加到excel表格中
        Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * excel 導出
     *
     * @param list         數據列表
     * @param pojoClass    pojo類型
     * @param fileName     導出時的excel名稱
     * @param response
     * @param exportParams 導出參數(標題、sheet名稱、是否創建表頭,表格類型)
     */
    private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) throws IOException {
        //把數據添加到excel表格中
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * excel 導出
     *
     * @param list         數據列表
     * @param pojoClass    pojo類型
     * @param fileName     導出時的excel名稱
     * @param exportParams 導出參數(標題、sheet名稱、是否創建表頭,表格類型)
     * @param response
     */
    public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, ExportParams exportParams, HttpServletResponse response) throws IOException {
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }

    /**
     * excel 導出
     *
     * @param list      數據列表
     * @param title     表格內數據標題
     * @param sheetName sheet名稱
     * @param pojoClass pojo類型
     * @param fileName  導出時的excel名稱
     * @param response
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response) throws IOException {
        defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, ExcelType.XSSF));
    }

    /**
     * 根據模板生成excel后導出
     *
     * @param templatePath 模板路徑
     * @param map          數據集合
     * @param fileName     文件名
     * @param response
     * @throws IOException
     */
    public static void exportExcel(TemplateExportParams templatePath, Map<String, Object> map, String fileName, HttpServletResponse response) throws IOException {
        Workbook workbook = ExcelExportUtil.exportExcel(templatePath, map);
        downLoadExcel(fileName, response, workbook);
    }

    /**
     * excel 導出
     *
     * @param list           數據列表
     * @param title          表格內數據標題
     * @param sheetName      sheet名稱
     * @param pojoClass      pojo類型
     * @param fileName       導出時的excel名稱
     * @param isCreateHeader 是否創建表頭
     * @param response
     */
    public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response) throws IOException {
        ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
        exportParams.setCreateHeadRows(isCreateHeader);
        defaultExport(list, pojoClass, fileName, response, exportParams);
    }


    /**
     * excel下載
     *
     * @param fileName 下載時的文件名稱
     * @param response
     * @param workbook excel數據
     */
    private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/vnd.ms-excel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * word下載
     *
     * @param fileName 下載時的文件名稱
     * @param response
     * @param doc
     */
    private static void downLoadWord(String fileName, HttpServletResponse response, XWPFDocument doc) throws IOException {
        try {
            response.setCharacterEncoding("UTF-8");
            response.setHeader("content-Type", "application/msword");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".docx", "UTF-8"));
            doc.write(response.getOutputStream());
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }


    /**
     * excel 導入
     *
     * @param file      excel文件
     * @param pojoClass pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Class<T> pojoClass) throws IOException {
        return importExcel(file, 1, 1, pojoClass);
    }

    /**
     * excel 導入
     *
     * @param filePath   excel文件路徑
     * @param titleRows  表格內數據標題行
     * @param headerRows 表頭行
     * @param pojoClass  pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (StringUtils.isBlank(filePath)) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setNeedSave(true);
        params.setSaveUrl("/excel/");
        try {
            return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("模板不能為空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }


    /**
     * excel 導入
     *
     * @param file       上傳的文件
     * @param titleRows  表格內數據標題行
     * @param headerRows 表頭行
     * @param pojoClass  pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (file == null) {
            return null;
        }
        try {
            return importExcel(file.getInputStream(), titleRows, headerRows, pojoClass);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 導入
     *
     * @param inputStream 文件輸入流
     * @param titleRows   表格內數據標題行
     * @param headerRows  表頭行
     * @param pojoClass   pojo類型
     * @param <T>
     * @return
     */
    public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
        if (inputStream == null) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(titleRows);
        params.setHeadRows(headerRows);
        params.setSaveUrl("/excel/");
        params.setNeedSave(true);
        try {
            return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("excel文件不能為空");
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }


    /**
     * excel轉html預覽
     *
     * @param filePath 文件路徑
     * @param response
     * @throws Exception
     */
    public static void excelToHtml(String filePath, HttpServletResponse response) throws Exception {
        ExcelToHtmlParams params = new ExcelToHtmlParams(WorkbookFactory.create(POICacheManager.getFile(filePath)), true);
        response.getOutputStream().write(ExcelXorHtmlUtil.excelToHtml(params).getBytes());
    }

    /**
     * word模板導出
     *
     * @param map
     * @param templatePath
     * @param fileName
     * @param response
     * @throws Exception
     */
    public static void WordTemplateExport(Map<String, Object> map, String templatePath, String fileName, HttpServletResponse response) throws Exception {
        XWPFDocument doc = WordExportUtil.exportWord07(templatePath, map);
        downLoadWord(fileName, response, doc);
    }

    /**
     * word模板導出多頁
     *
     * @param list
     * @param templatePath
     * @param fileName
     * @param response
     * @throws Exception
     */
    public static void WordTemplateExportMorePage(List<Map<String, Object>> list, String templatePath, String fileName, HttpServletResponse response) throws Exception {
        XWPFDocument doc = new ParseWord07().parseWord(templatePath, list);
        downLoadWord(fileName, response, doc);
    }

    /**
     * excel 導入,有錯誤信息
     *
     * @param file      上傳的文件
     * @param pojoClass pojo類型
     * @param <T>
     * @return
     */
    public static <T> ExcelImportResult<T> importExcelMore(MultipartFile file, Class<T> pojoClass) throws IOException {
        if (file == null) {
            return null;
        }
        try {
            return importExcelMore(file.getInputStream(), pojoClass);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * excel 導入
     *
     * @param inputStream 文件輸入流
     * @param pojoClass   pojo類型
     * @param <T>
     * @return
     */
    private static <T> ExcelImportResult<T> importExcelMore(InputStream inputStream, Class<T> pojoClass) throws IOException {
        if (inputStream == null) {
            return null;
        }
        ImportParams params = new ImportParams();
        params.setTitleRows(1);//表格內數據標題行
        params.setHeadRows(1);//表頭行
        params.setSaveUrl("/excel/");
        params.setNeedSave(true);
        params.setNeedVerify(true);
        try {
            return ExcelImportUtil.importExcelMore(inputStream, pojoClass, params);
        } catch (NoSuchElementException e) {
            throw new IOException("excel文件不能為空");
        } catch (Exception e) {
            throw new IOException(e.getMessage()); } }
}

3)導入驗證構造器的依賴

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.4.0.Final</version>
        </dependency>

4)創建easypoi的工具類

package com.example.easypoidemoadmin.utils;

import cn.afterturn.easypoi.excel.annotation.Excel;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * easypoi工具類,
 * 使用new方式創建對象並使用
 *
 * @param <T>
 */
public class EasyPoiTool<T> {

    /**
     * 需要被反射的對象,使用泛型規范傳入對象
     */
    public T t;

    /**
     * 修改注解@Excel的屬性值
     * @param attributeName
     * @param columnName
     * @param targetValue
     * @throws Exception
     */
    public void changeAttribute(String attributeName, String columnName, Object targetValue) throws Exception {
        if (t == null) {
            throw new ClassNotFoundException("未找到目標類");
        }
        if (StringUtils.isEmpty(attributeName)) {
            throw new NullPointerException("傳入的注解屬性為空");
        }
        if (StringUtils.isEmpty(columnName)) {
            throw new NullPointerException("傳入的屬性列名為空");
        }
        //獲取目標對象的屬性值
        Field field = t.getClass().getDeclaredField(columnName);
        //獲取注解反射對象
        Excel excelAnion = field.getAnnotation(Excel.class);
        //獲取代理
        InvocationHandler invocationHandler = Proxy.getInvocationHandler(excelAnion);
        Field excelField = invocationHandler.getClass().getDeclaredField("memberValues");
        excelField.setAccessible(true);
        Map memberValues = (Map) excelField.get(invocationHandler);
        memberValues.put(attributeName, targetValue);
    }
}

3.2實戰演練

需求:對導入的學生信息進行驗證,驗證通過后才能導入。要求學生姓名不能為空,出生日期必須是yyyy-MM-dd格式,年齡必須合法。導入后把驗證未通過的信息通過excel方式再下載到本地。

1)新建學生對象,添加注解驗證並實現IExcelModel接口

package com.example.easypoidemoadmin.entity;

import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.handler.inter.IExcelModel;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.util.Date;

@Data
@TableName(value = "student")
public class Student implements IExcelModel {

    /**
     * id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
    @TableField(value = "name")
    @Excel(name = "姓名", width = 20)
    @NotNull(message = "姓名不能為空")
    private String name;

    /**
     * 年齡
     */
    @TableField(value = "age")
    private Integer age;

    /**
     * 年齡驗證
     */
    @TableField(exist = false)
    @Excel(name = "年齡")
    @NotNull(message = "年齡不能為空")
    @Pattern(regexp = "^(?:[1-9][0-9]?|1[01][0-9]|120)$", message = "年齡必須是整數,且在1-120之間")
    private String ageStr;

    /**
     * 出生日期
     */
    @TableField(value = "birth")
    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
    private Date birth;

    /**
     * 出生日期驗證
     */
    @TableField(exist = false)
    @Excel(name = "出生日期", isImportField = "true", importFormat = "yyyy-MM-dd", databaseFormat = "yyyy-MM-dd", width = 30)
    @NotNull(message = "出生日期不能為空")
    @Pattern(regexp = "^\\d{4}-\\d{1,2}-\\d{1,2}$", message = "日期格式必須是yyyy-MM-dd格式,如2020-01-01")
    private String birthStr;

    //錯誤信息
    @TableField(exist = false)
    @Excel(name = "錯誤信息", width = 50, isColumnHidden = true)
    private String errorMsg;


}

實現此接口的原因是獲取其驗證的錯誤信息,並將其映射到字段errorMsg上,當對象不包含此字段時,就看不到錯誤信息。

2)新建接口StudentController

package com.example.easypoidemoadmin.controller;

import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.example.easypoidemoadmin.entity.CommonResult;
import com.example.easypoidemoadmin.entity.Student;
import com.example.easypoidemoadmin.service.StudentService;
import com.example.easypoidemoadmin.utils.EasyPoiTool;
import com.example.easypoidemoadmin.utils.ExcelUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/student")
public class StudentController {

    @Autowired
    private StudentService studentService;

    /**
     * 查詢用戶信息列表
     *
     * @param name
     * @param page
     * @param limit
     * @return
     */
    @GetMapping("/list")
    public CommonResult getList(String name, Integer page, Integer limit) {
        IPage<Student> iPage = studentService.getList(name, page, limit);
        return new CommonResult(200, "查詢信息成功", iPage.getRecords(), iPage.getTotal());
    }

    /**
     * 導入學生信息
     *
     * @param file
     * @param response
     * @return
     */
    @PostMapping("/upload")
    public CommonResult upload(@RequestParam("file") MultipartFile file, HttpServletResponse response) {
        try {
            ExcelImportResult<Student> importResult = ExcelUtils.importExcelMore(file, Student.class);
            //驗證通過的數據
            List<Student> list = importResult.getList();
            //驗證未通過的數據
            List<Student> failList = importResult.getFailList();
            studentService.insertBatch(list);
            if (failList != null && failList.size() > 0) {
                //修改導出的日期格式
                EasyPoiTool<Student> easyPoiUtil = new EasyPoiTool<>();
                easyPoiUtil.t = failList.get(0);
                //展示錯誤的列
                easyPoiUtil.changeAttribute("isColumnHidden", "errorMsg", false);
                //設置導出的格式
                easyPoiUtil.changeAttribute("exportFormat", "birthStr", "");
                //導出excel
                String title = "導入異常的數據";
                ExcelUtils.exportExcel(failList, title, title, Student.class, title, response);
                return null;
            }
            return new CommonResult(200, "信息導入成功");
        } catch (Exception e) {
            e.printStackTrace();
            return new CommonResult(444, "信息導入失敗");
        }

    }

    @PostMapping("/exportTemplate")
    public void exportTemplate(@RequestBody Map<String, Object> map, HttpServletResponse response) throws IOException {
        List<Student> list = new ArrayList<>();
        ExcelUtils.exportExcel(list, (String) map.get("title"), (String) map.get("sheetName"), Student.class, (String) map.get("fileName"), response);
    }
}

對於后面的service和dao詳見源碼。

3)導入的頁面見源碼,這里主要說明導入的方法,在導入后需要根據返回的數據判斷是否有錯誤的信息,如果有則下載錯誤信息,若沒有則顯示成功。

      importExcel(param) {
        const file = param.file
        if (file.name.lastIndexOf('.') < 0) {
          this.$message.error('上傳文件只能是xls、xlsx格式!')
          return
        }
        const testMsg = file.name.substring(file.name.lastIndexOf('.') + 1).toLowerCase()
        const extensionXLS = testMsg == 'xls'
        const extensionXLSX = testMsg == 'xlsx'
        if (!extensionXLS && !extensionXLSX) {
          this.$message.error('上傳文件只能是xls、xlsx格式!')
          return
        }
        const isLt2M = file.size / 1024 / 1024 < 2
        if (!isLt2M) {
          this.$message.error('上傳文件不能超過 2MB!')
          return
        }
        this.importLoading = true
        const formData = new FormData()
        formData.append('file', param.file)
        student.upload(formData).then(res => {
          if (!res.code) {
            this.$message.error("部分數據導入失敗,數據已下載到本地,請查看!")
            fileDownload(res, '導入異常的數據.xlsx')
            this.fileList = []
            this.getList()
          } else if (res.code == 200) {
            this.$message.success("導入成功")
            this.fileList = []
            this.getList()
          } else {
            this.$message.error("導入失敗")
          }
        }).catch(err => {
          console.log(err)
          this.$message.error("導入失敗")
        }).finally(()=>{
          this.importLoading = false
        })
      },

也就是說對於這個上傳的請求,當返回的內容是json字符串時就是成功的,沒有錯誤的數據,若不是則返回的是arraybuff類型的數據,需要直接下載。

3.3注意事項

1)由於需要進行驗證,因此在工具類中必須要設置ImportParams的needVerify為true;

2)easypoi是使用springboot對應的版本,對於spring的版本,驗證在這里可能不生效;

3)對於驗證構造器hibernate的版本,springboot2對應的版本必須是5及以上,否則錯誤信息不會顯示;

4)對應上傳的方法,響應類型必須是arraybuff,否則下載的excel無法打開

5)要顯示錯誤的信息,必須設置errorMsg上@Excel注解的isColumnHidden為false

6)在@Excel中沒有設置導出(exportFormat)的日期格式,而是在需要導出的時候再通過反射的方式(調用EasyPoiUtil的方法)設置。若提前設置了,在導入時,輸入的格式不正確,在導出錯誤信息時則會拋出異常。

7)其自帶的正則驗證,要求字段的類型必須是字符串類型,其他類型會發生異常。因此,需要設置兩個字段,一個映射數據庫的字段,一個用於導出和導出。當然也可以使用兩個類進行分布對應。

8)當需要獲取錯誤的行號時,讓實體類繼承IExcelDataModel類並添加int類型的rowNum屬性即可。


免責聲明!

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



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