Jeecg-autopoi

1. 导入依赖

版本可根据自己选择

 <dependency>
     <groupId>org.jeecgframework</groupId>
     <artifactId>autopoi-web</artifactId>
     <version>1.3.4</version>
 </dependency>

2. 在实体类上添加注解

@Data
public class SysUser implements Serializable {

    /**id*/
    private String id;

    /**登录账号 */
    @Excel(name = "登录账号", width = 15)
    private String username;

    /**真实姓名*/
    @Excel(name = "真实姓名", width = 15)
    private String realname;

    /**头像*/
    @Excel(name = "头像", width = 15)
    private String avatar;

    /**生日*/
    @Excel(name = "生日", width = 15, format = "yyyy-MM-dd")
    private Date birthday;

    /**性别(1:男 2:女)*/
    @Excel(name = "性别", width = 15,dicCode="sex")
    private Integer sex;

    /**电子邮件*/
    @Excel(name = "电子邮件", width = 15)
    private String email;

    /**电话*/
    @Excel(name = "电话", width = 15)
    private String phone;

    /**状态(1:正常  2:冻结 )*/
    @Excel(name = "状态", width = 15,replace={"正常_1","冻结_0"})
    private Integer status;

3. 导入Excel

 	@ApiOperation(value = "导入")
    @PostMapping("importExcel")
    public void importExcel(MultipartFile file) {
    	//设置参数
        ImportParams params = new ImportParams();
        params.setTitleRows(2); // 表格标题行数,默认0
        params.setHeadRows(2); // 设置表头行数,默认1
        params.setNeedSave(true);
        try {
            long start = System.currentTimeMillis();
            List<SysUser> list = ExcelImportUtil.importExcel(file.getInputStream(), SysUser.class, params);
            //获取到list然后存入数据库
            list.forEach(a -> {
                sysUserMapper.insert(a);
            });
            log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
        } catch (Exception e) {
          	log.error(e.getMessage(), e);
            e.printStackTrace();
        } finally {
            try {
                file.getInputStream().close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }   
    }

4. 导出Excel

	@ApiOperation(value = "导出")
    @GetMapping("exportExcel")
    public ModelAndView exportExcel(HttpServletRequest request, HttpServletResponse response) {
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        List<Activity> pageList = activityMapper.selectList(null);
        //导出文件名称
        mv.addObject(NormalExcelConstants.FILE_NAME, "导出Excel文件名字");
        //注解对象Class
        mv.addObject(NormalExcelConstants.CLASS, Activity.class);
        //自定义表格参数
        mv.addObject(NormalExcelConstants.PARAMS, new ExportParams("自定义导出Excel模板内容标题",
                "导出人:ddz", "自定义Sheet名字"));
        //导出数据列表
        mv.addObject(NormalExcelConstants.DATA_LIST, pageList);
        return mv;
    }

参考文档:JEECG开源社区

EasyExcel

1. 导入依赖

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>

2. 宽度自适应策略

我这里采用的是自适应宽度,如果想单独设置每个字段的宽度,可以在实体类字段上加注解。

import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.Cell;

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

/**
 * 宽度自适应策略
 *
 * @author Windows
 */
public class ExcelWidthStyleStrategy extends AbstractColumnWidthStyleStrategy {

    // 单元格的最大宽度
    private static final int MAX_COLUMN_WIDTH = 200;
    // 缓存(第一个Map的键是sheet的index, 第二个Map的键是列的index, 值是数据长度)
    private final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>(8);

    // 重写设置列宽的方法
    @Override
    protected void setColumnWidth(WriteSheetHolder writeSheetHolder,
                                  List<WriteCellData<?>> cellDataList,
                                  Cell cell,
                                  Head head,
                                  Integer relativeRowIndex,
                                  Boolean isHead) {
        boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
        // 当时表头或者单元格数据列表有数据时才进行处理
        if (needSetWidth) {
            Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>(16));
            // 获取数据长度
            Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
            if (columnWidth >= 0) {
                if (columnWidth > MAX_COLUMN_WIDTH) {
                    columnWidth = MAX_COLUMN_WIDTH;
                }
                // 确保一个列的列宽以表头为主,如果表头已经设置了列宽,单元格将会跟随表头的列宽
                Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());

                if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
                    maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
                    // 如果使用EasyExcel默认表头,那么使用columnWidth * 512
                    // 如果不使用EasyExcel默认表头,那么使用columnWidth * 256
                    // 如果是自己定义的字体大小,可以再去测试这个参数常量
                    writeSheetHolder
                            .getSheet()
                            .setColumnWidth(cell.getColumnIndex(), columnWidth * 512);
                }

            }
        }
    }

    /**
     * 获取当前单元格的数据长度
     */
    private Integer dataLength(List<WriteCellData<?>> cellDataList,
                               Cell cell,
                               Boolean isHead) {
        if (isHead) {
            return cell.getStringCellValue().getBytes().length;
        } else {
            WriteCellData 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;
                }
            }
        }
    }

}

3. 工具类

import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.lang.Nullable;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;

/**
 * 导出工具类
 *
 * @author Windows
 */
public class ExcelUtils {
    /**
     * 导出公共方法
     *
     * @param datas    要导出的数据集
     * @param clazz    导出的实体
     * @param fileName 导出文件名称
     * @throws IOException
     */
    public static void export(List datas, @Nullable Class<?> clazz, String fileName) throws IOException {
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = sra.getResponse();
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyExcel没有关系
        //response.setContentType("application/json;charset=utf-8");
        //response.setCharacterEncoding("utf-8");
        // 设置文件名
        String exportFileName = URLEncoder.encode(fileName + DateUtil.today(), "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + exportFileName + ExcelTypeEnum.XLSX.getValue());
        // 创建一个写出的单元格样式对象
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        // 设置水平对齐方式
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 设置填充前景色
        headWriteCellStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
        // 创建写出Excel的字体对象
        WriteFont headWriteFont = new WriteFont();
        //设置字体高度
//        headWriteFont.setFontHeightInPoints((short) 10);
        headWriteCellStyle.setWriteFont(headWriteFont);
        headWriteCellStyle.setWrapped(true);
        // 创建一个写出的单元格样式对象
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //设置内容靠中对齐
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        // 边框设置
//        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);             // 设置单元格上边框为细线
//        contentWriteCellStyle.setBorderBottom(BorderStyle.THICK);         // 设置单元格下边框为粗线
//        contentWriteCellStyle.setBorderLeft(BorderStyle.MEDIUM);         // 设置单元格左边框为中线
//        contentWriteCellStyle.setBorderRight(BorderStyle.MEDIUM_DASHED); // 设置单元格右边框为中虚线
        // 设置行样式
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
        // 设置自定义自适应列宽样式
        ExcelWidthStyleStrategy excelWidthStyleStrategy = new ExcelWidthStyleStrategy();
        // 如果不用模板的方式导出的话,是doWrite
        EasyExcel.write(response.getOutputStream(), clazz)
                .excelType(ExcelTypeEnum.XLSX)
//                .password("123456")
                .autoCloseStream(true)
                .registerWriteHandler(horizontalCellStyleStrategy)
//                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .registerWriteHandler(excelWidthStyleStrategy)
                .sheet(fileName)     // 设置sheet名称
                .doWrite(datas);
    }

    /**
     * 根据Excel模板,批量导入数据
     *
     * @param file  导入的Excel
     * @param clazz 解析的类型
     * @return 解析完成的数据
     */
    public static List<?> importExcel(MultipartFile file, Class<?> clazz) {
        if (file == null || file.isEmpty()) {
            throw new RuntimeException("没有文件或者文件内容为空!");
        }
        List<?> dataList;
        BufferedInputStream ipt;
        try {
            InputStream is = file.getInputStream();
            // 用缓冲流对数据流进行包装
            ipt = new BufferedInputStream(is);
            // 数据解析监听器
            ExcelListener<?> listener = new ExcelListener<>();
            // 读取数据
            EasyExcel.read(ipt, clazz, listener).sheet().doRead();
            // 获取去读完成之后的数据
            dataList = listener.getList();
        } catch (Exception e) {
            throw new RuntimeException("数据导入失败!" + e);
        }
        return dataList;
    }
}

4. 在实体类上添加注解

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import lombok.AllArgsConstructor;
import lombok.Data;

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

/**
 * 测试实体类
 * 使用注解单独给字段设置宽度会覆盖自定义宽度策略
 *
 * @author ddz
 */
@Data
@AllArgsConstructor
//@ColumnWidth(value = 50) // 宽
public class User1 implements Serializable {

    @ExcelProperty(value = "姓名")
//    @ColumnWidth(value = 20)
    private String name;

    @ExcelProperty(value = "年龄")
//    @ColumnWidth(value = 50)
    private Integer age;

    @ExcelProperty(value = "生日")
//    @ColumnWidth(value = 100)
    @DateTimeFormat(value = "YYYY-MM-dd")
    private Date birthday;

    @ExcelProperty(value = "性别")
//    @ColumnWidth(50)
    private Integer gender;

    @ExcelProperty(value = "余额")
    @NumberFormat(value = "¥0.000")
    private Double balance;
}

5. 导出Excel

    @GetMapping("export")
    public void export() throws IOException {
    	// 这里我使用了假数据
        List<User1> users = new ArrayList<>();
        users.add(new User1("张三", 18, DateUtil.date(), 1,11.12));
        users.add(new User1("李四", 22, DateUtil.date(), 0,11D));
        users.add(new User1("王五", 26, DateUtil.date(), 0,11.121212));
        ExportUtils.export(users, User1.class, "自定义文件名");
    }

6. 导入Excel

1. 添加导入监听类

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Excel数据解析监听器, 数据解析方法异步执行
 *
 * @param <T> Excel中数据的类型
 * @author Windows
 */
@Getter
@Setter
@NoArgsConstructor
public class ExcelListener<T> extends AnalysisEventListener<T> {
    // 加入一个判断标签,判断数据是否已经读取完
    private volatile boolean retryLock = false;

    // 解析完成后的数据集合, 监听对象初始化之后,立即初始化集合对象
    private final List<T> dataList = new ArrayList<>();

    // 每次最多导入条数
    private final int batchSize = 2000;


    /**
     * 获取解析后的数据集合, 如果数据还没有被解析完成,会对读取该集合的线程进行阻塞,直到数据读取完成之后,进行解锁。
     * 如果一次导入数据超过batchSize条,则以抛异常的形式阻止导入数据
     *
     * @return 解析后的数据集合
     */
    public List<T> getList() {
        while (true) {
            if (retryLock) {
                if (dataList.size() > batchSize) {
                    // 手动清空数据内存数据,减少内存消耗
                    dataList.clear();
                    throw new RuntimeException("一次最多导入" + batchSize + "条数据");
                } else {
                    return dataList;
                }
            }
        }
    }

    /**
     * Excel每解析一行数据,就会调用一次该方法
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context analysis context
     */
    @Override
    public void invoke(T data, AnalysisContext context) {
        dataList.add(data);
    }

    /**
     * 读取表头内容
     *
     * @param headMap 表头部数据
     * @param context 数据解析上下文
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        // 在此处可以进行表头的校验,headMap对应的就是表头的数据
        // 在此处有个很奇怪的地方,就是当用户导入的文件没有任何表头的时候,也就是表头这一行为空
        // 表头检验则不会进行这个函数,且数据导入成功,所以这个地方要确认清楚
        // 最后我是在拿到所有数据,包括表头,再取第一行为表头进行校验
        //System.out.println("表头:" + headMap);
    }

    /**
     * 流中的数据解析完成之后,就会调用此方法
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 数据解析完成,解锁
        retryLock = true;
    }

    /**
     * 解析过程如果发生异常,会调用此方法
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        throw new RuntimeException("Excel数据异常,请检查或联系管理员!");
    }
}
 

2. 导入Excel

    @PostMapping("importExcel")
    public void importExcel(MultipartFile file) {
        List<?> objects = ExportUtils.importExcel(file, User1.class);
        System.out.println(objects);
    }
GitHub 加速计划 / ea / easyexcel
31.64 K
7.47 K
下载
快速、简洁、解决大文件内存溢出的java处理Excel工具
最近提交(Master分支:3 个月前 )
c42183df Bugfix 3 个月前
efa7dff6 * 重新加回 `commons-io` 3 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐