SpringBoot 整合Easy Poi 下載Excel(標題帶批註)、導出Excel(帶圖片)、導入Excel(校驗參數,批註導出),附案例源碼

導讀

  日常開發過程中,經常遇到Excel導入、導出等功能,其中導入邏輯相對麻煩些,還涉及到參數的校驗,然後將錯誤信息批註導出。之前寫過EasyExcel導入(參數校驗,帶批註)(點我直達1點我直達2)、導出等功能。今天遇到一個需求是,導入、導出還需要帶上圖片,EasyExcel目前還不支持Excel中帶圖片的。Easy Poi支持帶圖片導入、導出,批註等功能,好啦~廢話不多說,下面開始叭~

項目源碼

添加依賴

            <!-- easy poi -->
            <dependency>
                <groupId>cn.afterturn</groupId>
                <artifactId>easypoi-spring-boot-starter</artifactId>
                <version>4.1.0</version>
            </dependency>
            <!-- JSR 303 規範驗證包 -->
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-validator</artifactId>
                <version>5.2.4.Final</version>
            </dependency>
            <!-- 阿里雲OSS -->
            <dependency>
                <groupId>com.aliyun.oss</groupId>
                <artifactId>aliyun-sdk-oss</artifactId>
                <version>3.10.2</version>
            </dependency>

友情提示

  • Easy Poi依賴版本大於4.1.0時,導出圖片顯示異常!!!!!這個坑了我好久 嗚嗚嗚嗚

  • Easy Poi導入時,支持hibernate的註解,校驗參數

實體類

導入實體類

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

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

/**
 * 人員信息Excel導出實體類
 *
 * @Author:chenyanbin
 */
@Data
public class PersonImportExcelDomain implements Serializable {
    /**
     * 姓名
     */
    @Excel(name = "姓名", height = 20, width = 30)
    @EasyPoiCellAnnotation(cellIndex = 0)
    @NotNull(message = "姓名不能爲空")
    private String userNick;

    /**
     * 性別
     * replace:替換枚舉值,1->男;2->女
     * suffix:爲每個值後,添加後綴
     */
    @Excel(name = "性別", replace = {"1_男", "2_女"}, suffix = "生")
    @EasyPoiCellAnnotation(cellIndex = 1)
    @Pattern(regexp = "男|女", message = "性別不能爲空 1->男|2->女")
    private String sex;

    /**
     * 出生日期
     */
    @Excel(name = "出生日期", databaseFormat = "yyyyMMddHHmmss", importFormat = "yyyy-MM-dd HH:mm:ss", format = "yyyy-MM-dd HH:mm:ss", width = 25)
    @EasyPoiCellAnnotation(cellIndex = 2)
    private Date birthday;

    /**
     * 頭像
     */
    @Excel(name = "頭像", type = 2, savePath = "/Users/chenyanbin/upload")
    @EasyPoiCellAnnotation(cellIndex = 3)
    @NotNull(message = "頭像不能爲空")
    private String pic;

    /**
     * 臨時頭像字節
     */
    private String tempPicUrl;
}

  注意:導入時,Easy Poi會將Excel和圖片,下載到宿主機中,此時該實體類中的pic爲當前宿主機圖片的絕對路徑,需要將圖片上傳至阿里雲Oss上,並將圖片url賦值到tempPicUrl

導出實體類

package com.yida.excel.domain;

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

import java.io.Serializable;
import java.util.Date;

/**
 * 人員信息Excel導出實體類
 * @Author:chenyanbin
 */
@Data
public class PersonExportExcelDomain implements Serializable {
    /**
     * 姓名
     */
    @Excel(name = "姓名", height = 20, width = 30)
    private String userNick;

    /**
     * 性別
     * replace:替換枚舉值,1->男;2->女
     * suffix:爲每個值後,添加後綴
     */
    @Excel(name = "性別", replace = {"男_1", "女_2"}, suffix = "生")
    private int sex;

    /**
     * 出生日期
     */
    @Excel(name = "出生日期", databaseFormat = "yyyyMMddHHmmss", importFormat = "yyyy-MM-dd HH:mm:ss", format = "yyyy-MM-dd HH:mm:ss",width = 25)
    private Date birthday;

    /**
     * 頭像
     */
    @Excel(name = "頭像", type = 2, width = 10, height = 10, imageType = 2)
    private byte[] pic;

    /**
     * 臨時頭像地址
     */
    private String tempPicUrl;
}

  注意:導出圖片時,需要將網絡圖片轉換成字節數組!做法:將tempPicUrl調用http工具類,轉換成字節數組,賦值到pic

導入校驗參數批註實體類

import lombok.Data;

/**
 * Excel 錯誤批註信息vo
 * @Author:chenyanbin
 */
@Data
public class ExcelErrorInfoVo {
    /**
     * 行索引,從0開始
     */
    private int rowIndex;
    /**
     * 列索引,從0開始
     */
    private int cellIndex;

    /**
     * 錯誤原因
     */
    private String reasonText;
}

工具類

Http工具類

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Http工具類
 *
 * @Author:chenyanbin
 */
@Slf4j
public class HttpUtil {

    /**
     * 獲取網絡圖片轉成字節流
     *
     * @param strUrl 完整圖片地址
     * @return 圖片資源數組
     */
    public static byte[] getNetImgByUrl(String strUrl) {
        try {
            URL url = new URL(strUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(2 * 1000);
            // 通過輸入流獲取圖片數據
            InputStream inStream = conn.getInputStream();
            // 得到圖片的二進制數據
            byte[] btImg = readInputStream(inStream);
            return btImg;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 從輸入流中獲取字節流數據
     *
     * @param inStream 輸入流
     * @return 圖片流
     */
    private static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        // 設置每次讀取緩存區大小
        byte[] buffer = new byte[1024 * 10];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        inStream.close();
        return outStream.toByteArray();
    }
}

添加批註工具類

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.lang.reflect.Field;
import java.util.*;

/**
 * Easy Poi Excel工具類
 *
 * @Author:chenyanbin
 */
public class EasyPoiExcelUtil {
    private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

    /**
     * 添加批註
     *
     * @param workbook       工作簿
     * @param titleRowsIndex 標題的行索引,從0計數
     * @param commentStr     批註格式: 0#姓名不能爲空__1#學生性別 1:男 2:女__2#出生日期:yyyy-MM-dd__3#圖片不能爲空
     */
    public static void buildComment(Workbook workbook, int titleRowsIndex, String commentStr) {
        Sheet sheet = workbook.getSheetAt(0);
        //創建一個圖畫工具
        Drawing<?> drawing = sheet.createDrawingPatriarch();
        Row row = sheet.getRow(titleRowsIndex);
        if (StringUtils.isNotBlank(commentStr)) {
            //解析批註,並傳換成map
            Map<Integer, String> commentMap = getCommentMap(commentStr);
            for (Map.Entry<Integer, String> entry : commentMap.entrySet()) {
                Cell cell = row.getCell(entry.getKey());
                //創建批註
                Comment comment = drawing.createCellComment(newClientAnchor(workbook));
                //輸入批註信息
                comment.setString(newRichTextString(workbook, entry.getValue()));
                //將批註添加到單元格對象中
                cell.setCellComment(comment);

//                //設置單元格背景顏色
//                CellStyle cellStyle = workbook.createCellStyle();
//                //設置顏色
//                cellStyle.setFillForegroundColor(IndexedColors.BLACK1.getIndex());
//                //設置實心填充
//                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//                cell.setCellStyle(cellStyle);
            }
        }
    }

    /**
     * 添加批註
     *
     * @param workbook      工作簿
     * @param errorInfoList 批註錯誤集合
     */
    public static void buildComment(Workbook workbook, List<ExcelErrorInfoVo> errorInfoList) {
        Sheet sheet = workbook.getSheetAt(0);
        //創建一個圖畫工具
        Drawing<?> drawing = sheet.createDrawingPatriarch();
        for (ExcelErrorInfoVo vo : errorInfoList) {
            Row row = sheet.getRow(vo.getRowIndex());
            if (StringUtils.isNotBlank(vo.getReasonText())) {
                Cell cell = row.getCell(vo.getCellIndex());
                //創建批註
                Comment comment = drawing.createCellComment(newClientAnchor(workbook));
                //輸入批註信息
                comment.setString(newRichTextString(workbook, vo.getReasonText()));
                //將批註添加到單元格對象中
                cell.setCellComment(comment);

//                //設置單元格背景顏色
//                CellStyle cellStyle = workbook.createCellStyle();
//                //設置顏色
//                cellStyle.setFillForegroundColor(IndexedColors.BLACK1.getIndex());
//                //設置實心填充
//                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
//                cell.setCellStyle(cellStyle);
            }
        }
    }

    /**
     * 校驗Excel數據
     *
     * @param obj      Excel中當前行的數據對象
     * @param clz      Excel中當前行的數據對象的類
     * @param rowIndex 該條記錄對應的行索引
     * @return
     */
    public static <T> List<ExcelErrorInfoVo> checkExcelData(T obj, Class<?> clz, int rowIndex) {
        List<ExcelErrorInfoVo> errorInfoList = new ArrayList<>();
        Set<ConstraintViolation<T>> cvSet = validator.validate(obj);
        Field f = null;
        for (ConstraintViolation<T> cv : cvSet) {
            try {
                f = clz.getDeclaredField(cv.getPropertyPath().toString());
                f.setAccessible(true);
                EasyPoiCellAnnotation annotation = f.getAnnotation(EasyPoiCellAnnotation.class);
                if (annotation == null) {
                    continue;
                }
                int cellIndex = annotation.cellIndex();
                ExcelErrorInfoVo vo = new ExcelErrorInfoVo();
                vo.setRowIndex(rowIndex);
                vo.setCellIndex(cellIndex);
                vo.setReasonText(cv.getMessage());
                errorInfoList.add(vo);
            } catch (NoSuchFieldException e) {
            } finally {
                if (f != null) {
                    f.setAccessible(false);
                }
            }
        }
        return errorInfoList;
    }

    /**
     * 批註信息,默認解析:批註#列索引,比如用戶名不允許重複#0。可覆蓋此方法,解析自定義的批註格式
     *
     * @param commentStr 當前行的所有批註信息
     * @return key:列索引,value:對應列的所有批註信息
     */
    protected static Map<Integer, String> getCommentMap(String commentStr) {
        //每行的所有單元格的批註都在commentStr裏,並用”__”分隔
        String[] split = commentStr.split("__");
        Map<Integer, String> commentMap = new HashMap<>();
        for (String msg : split) {
            String[] cellMsg = msg.split("#");
            //如果當前列沒有批註,會將該列的索引作爲key存到map裏;已有批註,以“,“分隔繼續拼接
            int cellIndex = Integer.parseInt(cellMsg[0]);
            if (commentMap.get(cellIndex) == null) {
                commentMap.put(cellIndex, cellMsg[1]);
            } else {
                commentMap.replace(cellIndex, commentMap.get(cellIndex) + "," + cellMsg[1]);
            }
        }
        return commentMap;
    }

    private static ClientAnchor newClientAnchor(Workbook workbook) {
        //xls
        if (workbook instanceof HSSFWorkbook) {
            return new HSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6);
        }
        //xlsx
        else {
            return new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6);
        }
    }

    private static RichTextString newRichTextString(Workbook workbook, String msg) {
        //xls
        if (workbook instanceof HSSFWorkbook) {
            return new HSSFRichTextString(msg);
        }
        //xlsx
        else {
            return new XSSFRichTextString(msg);
        }
    }
}

自定義註解

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface EasyPoiCellAnnotation {
    /**
     * 字段索引位置,從0開始計數
     * @return
     */
    int cellIndex();
}

阿里雲Oss文件上傳

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * 阿里雲oss配置文件
 * @Author:chenyanbin
 */
@Data
@Configuration
@ConfigurationProperties(prefix = "aliyun.oss")
public class OssConfig {

    private String endpoint;

    private String accessKeyId;

    private String accessSecret;

    private String bucketName;
}
import org.springframework.web.multipart.MultipartFile;

import java.io.File;

/**
 * 阿里雲oss文件上傳service
 * @Author:chenyanbin
 */
public interface OssFileService {
    /**
     * 上傳文件
     * @param file
     * @return
     */
    String uploadFile(MultipartFile file);

    /**
     * 上傳客戶端本地文件
     * @param file
     * @return
     */
    String uploadClientFile(File file);
}
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @Author:chenyanbin
 */
@Service
@Slf4j
public class OssFileServiceImpl implements OssFileService {
    @Autowired
    private OssConfig ossConfig;

    @Override
    public String uploadFile(MultipartFile file) {
        //獲取相關配置
        String bucketName = ossConfig.getBucketName();
        String endpoint = ossConfig.getEndpoint();
        String accessKeyId = ossConfig.getAccessKeyId();
        String accessSecret = ossConfig.getAccessSecret();
        //創建OSS對象
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessSecret);
        //原始文件名稱  xxx.jpg
        String originalFileName = file.getOriginalFilename();
        //JDK8 日期格式化
        LocalDateTime ldt = LocalDateTime.now();
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        //文件路徑
        String folder = dtf.format(ldt);
        //拼裝路徑,oss上存儲的路徑 2021/05/16/xxx.jpg
        String fileName = CommonUtil.generateUUID();
        //擴展名
        String extension = originalFileName.substring(originalFileName.lastIndexOf("."));
        String newFileName = "user/" + folder + "/" + fileName + extension;
        //推送到oss
        try {
            PutObjectResult putObjectResult = ossClient.putObject(bucketName, newFileName, file.getInputStream());
            //拼裝返回路徑
            if (putObjectResult != null) {
                String imgUrl = "https://" + bucketName + "." + endpoint + "/" + newFileName;
                return imgUrl;
            }
        } catch (IOException e) {
            log.error("oss文件上傳失敗:{}", e);
        } finally {
            //oss關閉
            ossClient.shutdown();
        }
        return null;
    }

    @Override
    public String uploadClientFile(File file) {
        try {
            //獲取相關配置
            String bucketName = ossConfig.getBucketName();
            String endpoint = ossConfig.getEndpoint();
            String accessKeyId = ossConfig.getAccessKeyId();
            String accessSecret = ossConfig.getAccessSecret();
            //創建OSS對象
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessSecret);
            //以輸入流的形式上傳文件
            InputStream is = new FileInputStream(file);
            //文件名
            String fileName = file.getName();
            //文件大小
            Long fileSize = file.length();
            //創建上傳Object的Metadata
            ObjectMetadata metadata = new ObjectMetadata();
            //上傳的文件的長度
            metadata.setContentLength(is.available());
            //指定該Object被下載時的網頁的緩存行爲
            metadata.setCacheControl("no-cache");
            //指定該Object下設置Header
            metadata.setHeader("Pragma", "no-cache");
            //指定該Object被下載時的內容編碼格式
            metadata.setContentEncoding("utf-8");
            //文件的MIME,定義文件的類型及網頁編碼,決定瀏覽器將以什麼形式、什麼編碼讀取文件。如果用戶沒有指定則根據Key或文件名的擴展名生成,
            //如果沒有擴展名則填默認值application/octet-stream
            metadata.setContentType(getContentType(fileName));
            //指定該Object被下載時的名稱(指示MINME用戶代理如何顯示附加的文件,打開或下載,及文件名稱)
            metadata.setContentDisposition("filename/filesize=" + fileName + "/" + fileSize + "Byte.");
            //JDK8 日期格式化
            LocalDateTime ldt = LocalDateTime.now();
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
            //文件路徑
            String folder = dtf.format(ldt);
            String newFileName = "user/" + folder + "/" + fileName;
            //上傳文件   (上傳文件流的形式)
            PutObjectResult putResult = ossClient.putObject(bucketName, newFileName, is, metadata);
            String imgUrl = "https://" + bucketName + "." + endpoint + "/" + newFileName;
            return imgUrl;
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * 通過文件名判斷並獲取OSS服務文件上傳時文件的contentType
     *
     * @param fileName 文件名
     * @return 文件的contentType
     */
    private String getContentType(String fileName) {
        //文件的後綴名
        String fileExtension = fileName.substring(fileName.lastIndexOf("."));
        if (".bmp".equalsIgnoreCase(fileExtension)) {
            return "image/bmp";
        }
        if (".gif".equalsIgnoreCase(fileExtension)) {
            return "image/gif";
        }
        if (".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension) || ".png".equalsIgnoreCase(fileExtension)) {
            return "image/jpeg";
        }
        if (".html".equalsIgnoreCase(fileExtension)) {
            return "text/html";
        }
        if (".txt".equalsIgnoreCase(fileExtension)) {
            return "text/plain";
        }
        if (".vsd".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.visio";
        }
        if (".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.ms-powerpoint";
        }
        if (".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) {
            return "application/msword";
        }
        if (".xml".equalsIgnoreCase(fileExtension)) {
            return "text/xml";
        }
        //默認返回類型
        return "image/jpeg";
    }
}
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.util.Random;
import java.util.UUID;

/**
 * 公共工具類
 *
 * @Author:chenyanbin
 */
@Slf4j
public class CommonUtil {
    /**
     * 生成uuid
     *
     * @return
     */
    public static String generateUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }

}

控制層

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @Author:chenyanbin
 */
@RestController
@RequestMapping("/api/excel/v1")
@Api(tags = "Easy Poi API測試")
public class ExcelController {
    @Autowired
    OssFileService ossFileService;

    @ApiOperation("下單Excel模板")
    @GetMapping("downloadExcelTemplate")
    public void downloadExcelTemplate(HttpServletResponse response) {
        //1、數據庫查詢數據,此處模擬數據
        List<PersonExportExcelDomain> personExcelList = new ArrayList<>();
        String fileName = "學生信息表.xls";
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("xxx班學生信息", "學生信息"), PersonExportExcelDomain.class, personExcelList);
        //標題加批註
        EasyPoiExcelUtil.buildComment(workbook, 1, "0#姓名不能爲空__1#學生性別 1:男 2:女__2#出生日期:yyyy-MM-dd__3#圖片不能爲空");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //mime類型
            response.setHeader("Pragma", "No-cache");//設置不要緩存
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            workbook.write(outputStream);
            outputStream.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @ApiOperation("導出Excel數據")
    @GetMapping("exportExcelTemplate")
    public void exportExcelTemplate(HttpServletResponse response) {
        //1、數據庫查詢數據,此處模擬數據
        List<PersonExportExcelDomain> personExcelList = new ArrayList<>();
        //學生:張三
        PersonExportExcelDomain person_1 = new PersonExportExcelDomain();
        person_1.setUserNick("張三");
        person_1.setSex(1);
        person_1.setBirthday(new Date());
        person_1.setTempPicUrl("https://images.cnblogs.com/cnblogs_com/chenyanbin/1560326/o_qianxun.jpg");
        personExcelList.add(person_1);
        //學生:李四
        PersonExportExcelDomain person_2 = new PersonExportExcelDomain();
        person_2.setUserNick("李四");
        person_2.setSex(2);
        person_2.setBirthday(new Date());
        person_2.setTempPicUrl("https://pic.cnblogs.com/face/1988848/20200625143435.png");
        personExcelList.add(person_2);
        //學生:王五
        PersonExportExcelDomain person_3 = new PersonExportExcelDomain();
        person_3.setUserNick("王五");
        person_3.setSex(2);
        person_3.setBirthday(new Date());
        personExcelList.add(person_3);
        //處理導出Excel圖片
        for (PersonExportExcelDomain domain : personExcelList) {
            if (StringUtils.isNotBlank(domain.getTempPicUrl())) {
                //將網絡圖片,轉換成文件流
                domain.setPic(HttpUtil.getNetImgByUrl(domain.getTempPicUrl()));
            }
        }
        String fileName = "xxx公司人員信息表.xls";
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("xxx部門人員表", "部門"), PersonExportExcelDomain.class, personExcelList);
        //標題加批註
        EasyPoiExcelUtil.buildComment(workbook, 1, "0#姓名不能爲空__1#性別 1:男 2:女__2#出生日期:yyyy-MM-dd__3#頭像不能爲空");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Pragma", "No-cache");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            workbook.write(outputStream);
            outputStream.flush();
        } catch (IOException e) {
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 導入Excel
     *
     * @param file
     * @param response
     */
    @PostMapping("importExcel")
    @ApiOperation("導入Excel")
    public void importExcel(
            @ApiParam(value = "文件上傳", required = true) @RequestPart("file") MultipartFile file,
            HttpServletResponse response
    ) throws Exception {
        //1、導入參數配置
        ImportParams params = new ImportParams();
        params.setNeedSave(true);
        params.setTitleRows(1);
        params.setSaveUrl("/Users/chenyanbin/upload");
        //2、獲取Excel數據(Excel中單元格中若存在圖片,EasyPoi會將圖片上傳到本機)
        List<PersonImportExcelDomain> personList = ExcelImportUtil.importExcel(file.getInputStream(), PersonImportExcelDomain.class, params);
        //3、處理上傳圖片,轉換至字節流
        List<ExcelErrorInfoVo> errorList = new ArrayList<>();
        for (int i = 0; i < personList.size(); i++) {
            //圖片不爲空
            if (StringUtils.isNotBlank(personList.get(i).getPic())) {
                String fileUrl = ossFileService.uploadClientFile(new File(personList.get(i).getPic()));
                personList.get(i).setTempPicUrl(fileUrl);
            }
            PersonImportExcelDomain domain = personList.get(i);
            //4、將校驗失敗的Excel添加至集合中
            errorList.addAll(EasyPoiExcelUtil.checkExcelData(domain, PersonImportExcelDomain.class, i + 2));
        }
        //5、Excel數據是否正確
        if (errorList.size() == 0) {
            //數據正確
            //TODO 導入數據庫
            personList.stream().forEach(System.err::println);
        } else {
            String fileName = "學生信息表導入-校驗錯誤.xls";
            //數據格式不正確,添加批註導出
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("xxx班學生信息", "學生信息"), PersonImportExcelDomain.class, personList);
            //標題加批註
            EasyPoiExcelUtil.buildComment(workbook, errorList);
            ServletOutputStream outputStream = null;
            try {
                outputStream = response.getOutputStream();
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); //mime類型
                response.setHeader("Pragma", "No-cache");//設置不要緩存
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                workbook.write(outputStream);
                outputStream.flush();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

演示

下載Excel模塊

  標題帶批註

帶圖片導出Excel

導入Excel

  參數校驗,帶批註導出

尾聲

  如果貼的代碼不全,請聯繫我微信~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章