springboot使用EasyExcel進行導入導出


1、添加依賴

        <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
        <dependency>
              <groupId>com.alibaba</groupId>
              <artifactId>easyexcel</artifactId>
              <version>2.1.1</version>
        </dependency>
        <!--xls-->
        <dependency>
              <groupId>org.apache.poi</groupId>
              <artifactId>poi</artifactId>
              <version>3.17</version>
        </dependency>
        <dependency>
              <groupId>org.apache.poi</groupId>
              <artifactId>poi-ooxml</artifactId>
              <version>3.17</version>
        </dependency>

2、創建導入導出的映射實體類

導出

package com.zl.model.vo;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Builder;
import lombok.Data;

/**
 * 導出VO
 *
 * @author z
 * @date 2022-03-14 18:01
 */
@Data
@HeadRowHeight(value = 20)
public class ExportModeSheetOneVO {
    /**
     * id  
     */
    @ExcelProperty(value = "編號",index = 0)
    @ColumnWidth(value = 20)
    private String id;

    /**
     * 名稱
     */
    @ExcelProperty(value = "名稱",index = 1)
    @ColumnWidth(value = 20)
    private String name;

    /**
     * 類型
     */
    @ExcelProperty(value = "類型",index = 2)
    @ColumnWidth(value = 20)
    private String type;

    /**
     * 描述
     */
    @ExcelProperty(value = "描述",index = 3)
    @ColumnWidth(value = 20)
    private String des;

    /**
     * 創建日期
     */
    @ExcelProperty(value = "創建日期",index = 4)
    @ColumnWidth(value = 20)
    private String createDt;

    /**
     * 創建者
     */
    @ExcelProperty(value = "創建者",index = 5)
    @ColumnWidth(value = 20)
    private String createBy;

    /**
     * 更新日期
     */
    @ExcelProperty(value = "更新日期",index = 6)
    @ColumnWidth(value = 20)
    private String updateDt;

    /**
     * 更新者
     */
    @ExcelProperty(value = "更新者",index = 7)
    @ColumnWidth(value = 20)
    private String updateBy;
}

導入

package com.zl.model.vo;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

/**
 * 導入VO
 *
 * @author z
 * @date 2022-03-15 16:08
 */
@Data
public class ImportModeVO {
    /**
     * 模型名稱
     */
    @ExcelProperty("名稱")
    private String name;
    /**
     * 類型
     */
    @ExcelProperty("類別")
    private String type;
    /**
     * 描述
     */
    @ExcelProperty("描述")
    private String des;
}

建議類型都寫為String,可以減少不必要的類型轉換

3、實現

Controller層代碼實現:

/**
 * 導入導出測試
 *
 * @author z
 * @since 2022-03-12
 */
@RestController
@RequestMapping("/myTest")
@Api(tags = "數據導入導出")
@Slf4j
public class MyInfoController {
    @Autowired
    private MyInfoService myInfoService; 
   /**
     * 模型導入
     * @param file
     * @return
     */
    @PostMapping("/importMode")
    @ApiOperation("導入")
    @ApiImplicitParam(name = "file",value = "上傳的數據",required = true)
    public Result importMode(@RequestParam("file") MultipartFile file){
        if (file==null||file.isEmpty()) {
            return Result.error();
        }
        int flag= 0;
        try {
            flag = myInfoService.importMode(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag==1?Result.success():Result.error();
    } 
 
   /**
     * 導出
     * @param type 導出類別
     */
    @GetMapping("/exportMode")
    @ApiOperation("導出")
    @ApiImplicitParam(name="type",value="導出類別",required = true)
    public void exportMode(String type, HttpServletResponse response){
        try {
            myInfoService.exportMode(type,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

   /**
     * 導出
     * @param type 導出類別
     */
    @GetMapping("/export")
    @ApiOperation("導出1")
    @ApiImplicitParam(name="type",value="導出類別",required = true)
    public void export(String type, HttpServletResponse response){
        try {
            myInfoService.export(type,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Service層實現:

    /**
     * 單個sheet導入
     * @param file
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int importMode(MultipartFile file) throws IOException {
        InputStream in =file.getInputStream();
        Integer readHeadIndex = 7;
        ImportExcelListenerUtil importExcelListenerUtil = new ImportExcelListenerUtil();
        importExcelListenerUtil.setHeadReadIndex(readHeadIndex);
        importExcelListenerUtil.setHeadEntity(new ImportModeVO());
        EasyExcel.read(in, ImportModeVO.class,importExcelListenerUtil).headRowNumber(readHeadIndex) // 從第幾行導入,此處為從第7行導入,即第7行是表頭      
                .sheet("Sheet1")
                .doRead();

        // 每次EasyExcel的read方法讀取完之后都會關閉流,我這里為了試驗doReadAll方法,所以重新獲取了一次
        // in = file.getInputStream();
        // SyncReadListener syncReadListener = new SyncReadListener();
        // EasyExcel.read(in,ImportModeVO.class,syncReadListener).doReadAll();
        List<ImportModeVO> list = importExcelListenerUtil.list;
        for(int i=0;i<list.size();i++){
            ImportModeEntity importModeEntity =new ImportModeEntity();
            BeanUtils.copyProperties(list.get(i),importModeEntity);
            importModeEntity.setType(Integer.parseInt(list.get(i).getType()));
            String modeId=UUID.randomUUID().toString().replace("-","").toUpperCase();
            importModeEntity.setId(modeId);
            this.save(importModeEntity);
        }
        in.close();
        return 1;
    }

    /**
     * 多個sheet導入
     * @param file
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int importMode(MultipartFile file) throws IOException {
        InputStream in = file.getInputStream();
        List<ReadSheet> sheets = EasyExcel.read(in).build().excelExecutor().sheetList();
        for (ReadSheet sheet : sheets) {
            //因為在獲取sheetList的時候改變了通過file獲取的流類型,在EasyExcel.read讀的時候,采用的同一個流,會導致excel類型出錯。
            //解決方法:重新創建一個excel流,與獲取sheet的流區別使用
            InputStream io = file.getInputStream();
            ImportExcelListenerUtil importExcelListenerUtil = new ImportExcelListenerUtil()
                    .setHeadReadIndex(readHeadIndex)
                    .setHeadEntity(new ImportModeVO());
            EasyExcel.read(io, ImportModeVO.class,importExcelListenerUtil)
                    .sheet(sheet.getSheetName())
                    .doRead();
            List<ImportModeVO> list = ImportExcelListenerUtil.list;
            for (int j = 0; j < list.size(); j++) {
                System.out.println(list.get(j));
            }
            System.out.println("這是" + sheet.getSheetName() + "的所有數據");
            ImportExcelListenerUtil.list.clear();
            io.close();
        }
        return 1;
    }

    /**
     * 模型導出
     * @param type 導出類別
     */
    @Override
    public void exportMode(String type, HttpServletResponse response) throws IOException {
        String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        // 導出文件名
        String fileName="文件導出"+time+".xlsx";
        // sheet名
        String sheetNameOne="導出sheet1";
        String sheetNameTwo="導出sheet2";
        //表頭樣式
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //內容樣式
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //設置內容靠左對齊
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
        //設置表頭居中對齊
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        fileName = URLEncoder.encode(fileName,"UTF-8");
        response.setHeader("Content-disposition","attachment;filename=" + fileName);
        // 查詢到數據
        List<ExportModeSheetOneVO> exportModeSheetOneVOS = this.selectModelShowOne(type);
		List<ExportModeSheetTwoVO> exportModeSheetTwoVOS = this.selectModelShowTwo(type);
        // 寫出
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
	// ExportModeSheetOneVO 是導出的映射實體類,exportModeSheetOneVOS 是導出的數據集合,registerWriteHandler(new CustomCellWriteHandlerUtil()) 設置表頭自適應,這是多個sheet導出,只有一個只需導出需要的sheet即可
        // 不設置自適應表頭:EasyExcel.writerSheet(0,sheetNameOne).head(ExportModeSheetOneVO.class).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(0,sheetNameOne).head(ExportModeSheetOneVO.class).registerWriteHandler(new CustomCellWriteHandlerUtil()).build();
        excelWriter.write(exportModeSheetOneVOS,writeSheet);
        writeSheet = EasyExcel.writerSheet(1,sheetNameTwo).head(ExportModeSheetTwoVO.class).registerWriteHandler(new CustomCellWriteHandlerUtil()).build();
        excelWriter.write(exportModeSheetTwoVOS,writeSheet);
        //千萬別忘記關流,finish會幫忙關流
        excelWriter.finish();
    }
  /**
   * 模型導出
   * @param type 導出類別
   */
  @Override
  public void export(String type, HttpServletResponse response) throws IOException {
        // 查詢數據
        List<ExportModeSheetOneVO> data= this.selectModelShowOne(type);
        // 設置響應
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String fileName = "文件導出";
        try {
            fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xls");
            // 添加過濾的列
            Set<String> columnNames = new HashSet<String>();
            columnNames.add("id");
            ...
            EasyExcel.write(response.getOutputStream(), ExportModeSheetOneVO.class).excludeColumnFiledNames(columnNames).sheet("導出sheet1").doWrite(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

直接導出文件:ExcelWriter excelWriter = EasyExcel.write(filePath).build(); 其中 filePath 為導出文件的目錄 eg:String filePath = "/home/數據導出.xlsx";

  • 導入需要設置一個自定義Easyexcel的監聽類
package com.zl.util;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;

import java.lang.reflect.Field;
import java.util.*;

public class ImportExcelListenerUtil extends AnalysisEventListener<Object> {
    private List<Object> list = new ArrayList<>();
    private Object headEntity;
    private Integer headReadIndex = 1;

    public List<Object> getList() {
        return list;
    }

    public ImportExcelListenerUtil setList(List<Object> list) {
        this.list = list;
        return this;
    }

    public Integer getHeadReadIndex() {
        return headReadIndex;
    }

    public ImportExcelListenerUtil setHeadReadIndex(Integer headReadIndex) {
        this.headReadIndex = headReadIndex;
        return this;
    }

    public Object getHeadEntity() {
        return headEntity;
    }

    public ImportExcelListenerUtil setHeadEntity(Object headEntity) {
        this.headEntity = headEntity;
        return this;
    }

    @Override
    public void invoke(Object excelEntity, AnalysisContext context) {
        list.add(excelEntity);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        //  String[] headList = {"序號", "姓名", "性別", "年齡", "文化程度", "民族", "身份證號", "工作單位/學校", "手機/電話", "備注"};
        if (ObjectUtil.isEmpty(headEntity)) {
            throw new ExcelAnalysisException("讀取系統模板實體為:null");
        }
        // 驗證表頭 獲取實體類注解@ExcelProperty標識的表頭,以index標識的順序進行比較
        if (context.readRowHolder().getRowIndex() == headReadIndex - 1) {
            // 獲取實體類表頭信息
            Map<Integer, String> entityHeadMap = new LinkedHashMap<>();
            List<Field> fieldList = TableInfoHelper.getAllFields(this.getHeadEntity().getClass());
            for (Field field : fieldList) {
                if (field.isAnnotationPresent(ExcelProperty.class)) {
                    ExcelProperty excel = field.getAnnotation(ExcelProperty.class);
                    //英文為key,中文為value
                    Integer key = excel.index();
                    String[] value = excel.value();
                    entityHeadMap.put(key, value.length > 0 ? value[value.length - 1] : "");
                }
            }
            // 判斷讀取表頭是否和實體表頭一致
            Set<Integer> heads = entityHeadMap.keySet();
            for (Integer headIndex : heads) {
                if (!entityHeadMap.get(headIndex).equals(headMap.get(headIndex))) {
                    throw new ExcelAnalysisException("上傳模板與系統模板不匹配,請使用平台模板上傳數據");
                }
            }
        }
        super.invokeHeadMap(headMap, context);
    }
}
  • 自定義表頭需要的工具類
package com.zl.util;

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 導出自適應列寬
 *
 * @author z
 * @date 2022-03-15 14:57
 */
public class CustomCellWriteHandlerUtil extends AbstractColumnWidthStyleStrategy {
    private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();
    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = CACHE.get(writeSheetHolder.getSheetNo());
            if (maxColumnWidthMap == null) {
                maxColumnWidthMap = new HashMap<>();
                CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
            }
            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth>= 0) {
                if (columnWidth> 255) {
                    columnWidth = 255;
                }
                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
                if (maxColumnWidth == null || columnWidth> maxColumnWidth) {
                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                    writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
                }
            }
        }
    }
    private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            CellData cellData = cellDataList.get(0);
            CellDataTypeEnum type = cellData.getType();
            if (type == null) {
                return -1;
            } else {
                switch (type) {
                    case STRING:
                        return cellData.getStringValue().getBytes().length;
                    case BOOLEAN:
                        return cellData.getBooleanValue().toString().getBytes().length;
                    case NUMBER:
                        return cellData.getNumberValue().toString().getBytes().length;
                    default:
                        return -1;
                }
            }
        }
    }
}

4、測試

  • 使用 postman 測試導入導出接口


免責聲明!

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



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