java 导入导出excel表格

2年前 (2022) 程序员胖胖胖虎阿
222 0 0

java 导入导出excel表格

业务上有需求上传excel表格并读取内容,本文记录一下该方法

表格导入

引入相应的工具包

    <dependency>
        <groupId>cn.afterturn</groupId>
        <artifactId>easypoi-annotation</artifactId>
        <version>4.4.0</version>
    </dependency>

    <dependency>
        <groupId>cn.afterturn</groupId>
        <artifactId>easypoi-base</artifactId>
        <version>4.4.0</version>
    </dependency>

    <dependency>
        <groupId>cn.afterturn</groupId>
        <artifactId>easypoi-web</artifactId>
        <version>4.4.0</version>
    </dependency>

要清楚表格的格式,这样才能建对应的实体类,例如,表格如下:

java 导入导出excel表格

要明确有3列,每列标题是什么,从第2行开始是正式的数据。

创建对应的实体类

注意使用@Excel注解,name即表格中列的名称,orderNum是列的顺序

import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Province {
    @Excel(name = "id", orderNum = "0")
    private String id;

    @Excel(name = "countryCode", orderNum = "1")
    private String countryCode;

    @Excel(name = "countryName", orderNum = "2", type = 10)
    private String countryName;

}

controller层写方法接收excel文件并解析

本文举例比较简单,实际项目中可以封装一个解析excel的工具类,controller层收到请求后在service层调用工具类解析,再处理相关逻辑。本文直接在controller层解析了,不写那么麻烦了。

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.Vo.Province;
import org.apache.commons.compress.utils.Lists;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;

@RestController
public class TestController {

    @PostMapping("/test/import")
    public void getList(@RequestPart MultipartFile file) {
        ImportParams params = new ImportParams();
        //从第一行开始解析
        params.setHeadRows(1);
        List<Province> provinces = Lists.newArrayList();
        try {
            //解析后每一行转换成Province对象
            provinces = ExcelImportUtil.importExcel(file.getInputStream(), Province.class, params);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(provinces);
    }
}

运行方法进行测试

引入swagger或者使用postman或其他方法调用该接口,传入excel,看是否打印正确。
本文使用postman进行测试,如图:

java 导入导出excel表格

控制台输出的结果如图,已经正确读到excel的内容。

java 导入导出excel表格

表格导出

创建对应的实体类

与导入表格类似,创建要导出表格的实体类,注意使用@Excel注解,标明列明和第几列。
比如我想导出一个手机品牌的excel,第一列是序号,第二列是手机品牌名称。

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PhoneVo {

    @Excel(name = "序号", orderNum = "1")
    private int code;

    @Excel(name = "手机品牌名称", orderNum = "1")
    private String name;
}

controller层写方法导出

本文简写,直接在controller层写逻辑方法了,可按实际情况放到service层写。
比如我用一个枚举列举所有手机品牌,将枚举的内容导出。


@Getter
public enum PhoneEnum {
    APPLE(0,"苹果"),
    HUAWEI(1,"华为"),
    MI(2,"小米"),
    OPPO(3,"oppo"),
    SAMSUNG(4,"三星"),
    ;


    private int code;
    private String type;

    PhoneEnum(int code, String type) {
        this.code = code;
        this.type = type;
    }
}

controller层先把枚举内容转换成list,然后调工具类接口生成excel。

@RestController
@RequestMapping("/")
public class TestController {

    @GetMapping("/test/download")
    public void getExcel(HttpServletResponse response) throws Exception {
    	// 表格名称
        String fileName = "手机品牌.xls";
        // 导出的表格title和sheet页名
        ExportParams exportParams = new ExportParams("手机品牌", "phone");
        // 导出的内容的list
        List<PhoneVo> list = getList();
        // 传参是要转换的对象类型,和list
        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, PhoneVo.class, list);
        if (workbook != null) {
            try {
                response.setCharacterEncoding("UTF-8");
                response.setHeader("content-Type", "application/vnd.ms-excel");
                response.setHeader("Content-Disposition",
                        "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
                workbook.write(response.getOutputStream());
            } catch (IOException e) {
                throw new Exception(e.getMessage());
            } finally {
                workbook.close();
            }
        }
    }

    private List<PhoneVo> getList(){
        List<PhoneVo> res = Lists.newArrayList();
        for (PhoneEnum phoneEnum : PhoneEnum.values()) {
            res.add(PhoneVo.builder().code(phoneEnum.getCode()).name(phoneEnum.getType()).build());
        }
        return res;
    }
}

测试

启动工程,在浏览器中输入url,回车,即可下载excel。

java 导入导出excel表格
导出结果如图片所示
java 导入导出excel表格

@Excel注解参数介绍

@Excel 注解源码对于参数的介绍

package cn.afterturn.easypoi.excel.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Excel 导出基本注释
 * @author JueYue
 *  2014年6月20日 下午10:25:12
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Excel {

    /**
     * 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式
     */
    public String databaseFormat() default "yyyyMMddHHmmss";

    /**
     * 导出的时间格式,以这个是否为空来判断是否需要格式化日期
     */
    public String exportFormat() default "";
    /**
     * 导入的时间格式,以这个是否为空来判断是否需要格式化日期
     */
    public String importFormat() default "";
    /**
     * 时间格式,相当于同时设置了exportFormat 和 importFormat
     */
    public String format() default "";
    /**
     * 时间时区
     */
    public String timezone() default "";
    /**
     * 数字格式化,参数是Pattern,使用的对象是DecimalFormat
     */
    public String numFormat() default "";

    /**
     * 导出时在excel中每个列的高度 单位为字符,一个汉字=2个字符
     * 优先选择@ExportParams中的 height
     */
    @Deprecated
    public double height() default 10;

    /**
     * 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的
     */
    public int imageType() default 1;

    /**
     * 文字后缀,如% 90 变成90%
     */
    public String suffix() default "";

    /**
     * 是否换行 即支持\n
     */
    public boolean isWrap() default true;

    /**
     * 合并单元格依赖关系,比如第二列合并是基于第一列 则{1}就可以了
     */
    public int[] mergeRely() default {};

    /**
     * 纵向合并内容相同的单元格
     */
    public boolean mergeVertical() default false;

    /**
     * 导出时,对应数据库的字段 主要是用户区分每个字段, 不能有annocation重名的 导出时的列名
     * 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用
     */
    public String name();

    /**
     * 导出时,表头双行显示,聚合,排序以最小的值参与总体排序再内部排序
     * 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用
     * 优先弱与 @ExcelEntity 的name和show属性
     */
    public String groupName() default "";

    /**
     * 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row)
     */
    public boolean needMerge() default false;

    /**
     * 展示到第几个可以使用a_id,b_id来确定不同排序
     */
    public String orderNum() default "0";

    /**
     * 值得替换  导出是{a_id,b_id} 导入反过来,所以只用写一个
     */
    public String[] replace() default {};
    /**
     *  字典名称
     */
    public String dict() default  "";

    /**
     * 下拉列表,仅支持replace和dict,dict优先
     * @return
     */
    public boolean addressList() default false;
    /**
     * 导入路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/
     *
     */
    public String savePath() default "/excel/upload/img";

    /**
     * 导出类型 1 是文本 2 是图片,3 是函数,10 是数字, 11 特殊符号 默认是文本
     */
    public int type() default 1;

    /**
     * 导出时在excel中每个列的宽 单位为字符,一个汉字=2个字符 如 以列名列内容中较合适的长度 例如姓名列6 【姓名一般三个字】
     * 性别列4【男女占1,但是列标题两个汉字】 限制1-255
     */
    public double width() default 10;

    /**
     * 是否自动统计数据,如果是统计,true的话在最后追加一行统计,把所有数据都和
     * 这个处理会吞没异常,请注意这一点
     * @return
     */
    public boolean isStatistics() default false;

    /**
     * 这个是不是超链接,如果是需要实现接口返回对象
     * @return
     */
    public boolean isHyperlink() default false;

    /**
     *  导入时会校验这个字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel
     *  本意是想用true的,想想还是false比较好
     *  可以使用a_id,b_id来确实是否使用
     * @return
     */
    public String isImportField() default "false";

    /**
     * 固定的某一列,解决不好解析的问题
     * @return
     */
    public int fixedIndex() default -1;

    /**
     *  是否需要隐藏该列
     * @return
     */
    public boolean isColumnHidden() default  false;

    /**
     * 枚举导出使用的字段
     * @return
     */
    public String enumExportField() default "";
    /**
     * 枚举导入使用的函数
     * @return
     */
    public String enumImportMethod() default "";
    /**
     * 数据脱敏规则
     * 规则1: 采用保留头和尾的方式,中间数据加星号
     * 如: 身份证  6_4 则保留 370101********1234
     *    手机号   3_4 则保留 131****1234
     * 规则2: 采用确定隐藏字段的进行隐藏,优先保留头
     * 如: 姓名   1,3 表示最大隐藏3位,最小一位
     *          李 -->  *
     *          李三 --> 李*
     *          张全蛋  --> 张*蛋
     *          李张全蛋 --> 李**蛋
     *          尼古拉斯.李张全蛋 -> 尼古拉***张全蛋
     * 规则3: 特殊符号后保留
     * 如: 邮箱    1~@ 表示只保留第一位和@之后的字段
     *        afterturn@wupaas.com -> a********@wupaas.com
     * 复杂版本请使用接口
     * {@link cn.afterturn.easypoi.handler.inter.IExcelDataHandler}
     */
    public String   desensitizationRule() default "";
}

测试几个常用的参数

修改导出excel例子中的PhoneVo,用来测试suffix,format,replace参数

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PhoneVo {

    @Excel(name = "序号", orderNum = "1")
    private int code;

		// suffix:后面加上“手机”  replace:replace = {"展示数据_替换数据"}
    @Excel(name = "手机品牌名称", orderNum = "1",suffix = "手机", replace = {"OPPO_oppo"})
    private String name;

		// format指定时间转换的格式
    @Excel(name = "时间", orderNum = "2",format = "yyyy-MM-dd-HH:mm:ss")
    private Timestamp time;
}

controller层方法修改如下

    private List<PhoneVo> getList() {
        List<PhoneVo> res = Lists.newArrayList();
        for (PhoneEnum phoneEnum : PhoneEnum.values()) {
            res.add(PhoneVo.builder().code(phoneEnum.getCode()).name(phoneEnum.getType()).time(new Timestamp(System.currentTimeMillis())).build());
        }
        return res;
    }

其余不动,在浏览器中输入url,回车,查看下载的excel。
java 导入导出excel表格

版权声明:程序员胖胖胖虎阿 发表于 2022年9月4日 上午9:00。
转载请注明:java 导入导出excel表格 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...