赞
踩
在做项目的过程中,外面经常会遇到一个问题,怎么才能把excel表中的数据实现批量的导入导出,使用的是EasyExcel进行操作。
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>easyexcel</artifactId>
- <version>3.1.3</version>
- </dependency>
原数据库字段过多,这边就截取一部分进行展示
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- @TableName("emp_user")
- public class EmpUserEntity implements Serializable {
- private static final long serialVersionUID = 1L;
- /**
- * 人员id
- */
- @ExcelIgnore
- private Long id;
- /**
- * 用户ID
- */
- @ExcelIgnore
- private Long userId;
- /**
- * 密码
- */
- @TableField(exist = false)
- @ExcelIgnore
- private String password;
- /**
- * 姓名
- */
- @ExcelProperty(value = {"姓名"}, index = 0)
- private String name;
- /**
- * 性别:1:男 0:女
- */
- @ExcelProperty(value = {"性别"}, index = 1,converter = SexConvert.class)
- //静态下拉框
- @ExcelSelected(source = {"男","女"})
- private Integer gender;
- /**
- * 年龄(计算退休时间)
- */
- @ExcelProperty(value = {"年龄"}, index = 2)
- private Integer age;
- /**
- * 电话
- */
- @ExcelProperty(value = {"电话"}, index = 3)
- @ColumnWidth(20)
- private String tel;
- /**
- * 邮箱
- */
- @ExcelProperty(value = {"邮箱"}, index = 4)
- @ColumnWidth(25)
- private String email;
- }
注释解释:
- @ExcelIgnore:EasyExcel框架中的注解,用于标识实体类中不需要导出或导入的属性。
-
- @ExcelProperty(value = {"性别"}, index = 1,converter = SexConvert.class):指定实体类与表格之间的映射关系
-
- value表示excel表格的标题列。
-
- index表示excel表格中的列索引。
-
- converter = SexConvert.class:指定类型转换器,将Excel表格中的数据转换成Java对象。
- @ExcelSelected(source = {"男","女"}):导出下拉框自定义注解
有些数据外面存入数据库的是数字,例如一般男生我们选用1,女生选用0,当时当我们进行表的输出和输入时,excel表中用的是男、女,我们采用类型转换器进行数据的转化。
- @ExcelProperty(value = {"性别"}, index = 1,converter = SexConvert.class)
- //SexConvert.class定义类转化器的类
- import com.alibaba.excel.converters.Converter;
- import com.alibaba.excel.enums.CellDataTypeEnum;
- import com.alibaba.excel.metadata.CellData;
- import com.alibaba.excel.metadata.GlobalConfiguration;
- import com.alibaba.excel.metadata.property.ExcelContentProperty;
-
- public class SexConvert implements Converter<Integer> {
- @Override
- //指定该转换器支持的 Java 类型。在本例中,返回 Integer.class,表示该转换器用于将 Excel 中的数据转换为整数类型。
- public Class supportJavaTypeKey() {
- return Integer.class;
- }
-
- @Override
- //指定该转换器支持的 Excel 数据类型。在本例中,返回 CellDataTypeEnum.STRING,表示该转换器处理的是字符串类型的 Excel 数据。
- public CellDataTypeEnum supportExcelTypeKey() {
- return CellDataTypeEnum.STRING;
- }
-
- @Override
- //将 Excel 中的数据转换为 Java 对象的方法。在本例中,该方法尚未实现,因此返回 null。你需要根据实际需求,实现具体的转换逻辑。
- public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
- return "男".equals(cellData.getStringValue()) ? 1 : 0;
- }
-
- @Override
- //将 Java 对象转换为 Excel 数据的方法。
- public CellData convertToExcelData(Integer value, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
- if (value == 1) {
- return new CellData("男");
- } else if (value == 0) {
- return new CellData("女");
- }
- // 如果值不是 Integer 或者不是合法的性别值,则抛出异常
- throw new IllegalArgumentException("无效的性别值");
- }
- }
其中第三个和第四个方法代表 java对象与excel表里的相互转换。
*注意包不要导错喔
- public class WebUserListener<T> extends AnalysisEventListener<T> {
-
- //首先使用 Logger 记录当前读取的数据信息,然后将读取到的数据对象 t 加入到 list 列表中,以备后续的获取
- public final Logger log = LoggerFactory.getLogger(this.getClass());
-
- public List<T> list = new ArrayList<>();
-
- /**
- * 该方法在读取到表格中的一条数据时被调用。其中 t 参数表示当前行的数据对象,analysisContext 参数表示解析过程的上下文对象。
- * @param t
- * @param
- */
- @Override
- public void invoke(T t, AnalysisContext analysisContext) {
- log.info("读取表格[{}]",t);
- list.add(t);
- }
-
- @Override
- public void doAfterAllAnalysed(AnalysisContext analysisContext) {
-
- }
- //获取数据,供controller层调用
- public List<T> getExcelData(){
- return list;
- }
-
- }
- @RestController
- @RequestMapping("emp/excel")
- public class EmpUserImport extends AbstractController {
-
- @Autowired
- private EmpUserService empUserService;
-
- @PostMapping("/Import")
- //@RequiresPermissions("emp:user:excelImport")
- public EmpUserEntity redExcel(MultipartFile multipartFile) throws IOException {
- //输入流指向了上传的 Excel 文件
- InputStream inputStream = multipartFile.getInputStream();
- //实例是一个 EasyExcel 的监听器,用于解析 Excel 数据。
- WebUserListener<EmpUserEntity> webListener = new WebUserListener<>();
- //用于处理 Excel 数据的监听器,表示对 Excel 中所有的 sheet 进行解析。
- EasyExcel.read(inputStream,EmpUserEntity.class,webListener).sheet().doRead();
- //获取数据对象列表。
- List<EmpUserEntity> excelData = webListener.getExcelData();
- //将获取到的数据填入数据库
- for (EmpUserEntity user : excelData) {
- empUserService.save(user);
- }
- return excelData ;
- }
- <el-form-item >
- <el-upload
- class="upload-demo"
- :action="excelImports"
- :headers="tokenInfo"
- :on-preview="handlePreview"
- :on-remove="handleRemove"
- :on-success="uploadSuccess"
- multiple
- name="multipartFile"
- :limit="3"
- >
- <el-button type="success" >点击上传</el-button>
- </el-upload>
- </el-form-item>
- data () {
- return {
- excelImports: this.$http.adornUrl('/emp/excel/Import'),
- tokenInfo: {
- 'token': this.$cookie.get('token')
- },
- dataForm: {
- deptId: '' //部门id
- },
- .........
- }
- }
- uploadSuccess (response, file, fileList) {
- this.getDataList()
- console.log(response)
- },
-
- handleRemove (file, fileList) {
- console.log(file, fileList)
- },
-
- handlePreview (file) {
- console.log(file)
- }
可实现导出附加功能:
1、后端代码
(1)创建实现下拉框选择的注解
- import java.lang.annotation.*;
-
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.FIELD)
- public @interface ExcelSelected {
-
- /**
- * 固定下拉内容
- */
- String[] source() default {};
-
- /**
- * 设置下拉框的起始行,默认为第二行
- */
- int firstRow() default 1;
-
- /**
- * 设置下拉框的结束行,默认为最后一行
- */
- int lastRow() default 0x10000;
-
- }
(2)自定义注解使用
- /**
- * 性别:1:男 0:女
- */
- @ExcelProperty(value = {"性别"}, index = 1,converter = SexConvert.class)
- //静态下拉框
- @ExcelSelected(source = {"男","女"})
- private Integer gender;
(2)创建导出工具类
- @Slf4j
- public class EasyExcelUtils {
- /**
- * 导出单sheet页且sheet页中含有下拉框的excel文件
- *
- * @param response HttpServletResponse
- * @param fileName 文件名
- * @param sheetName sheet页名
- * @param data 要导出的数据
- */
- public static <T> void writeExcelBySelect(HttpServletResponse response, String fileName, String sheetName, List<T> data) {
- try {
- encodeFileName(response, fileName);
- Map<Integer, ExcelSelected> integerExcelSelectedMap = resolveSelectedAnnotation(IterUtil.getElementType(data));
- EasyExcel.write(response.getOutputStream(), IterUtil.getElementType(data))
- .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
- .registerWriteHandler(selectedSheetWriteHandler(integerExcelSelectedMap))
- .sheet(StringUtils.isEmpty(sheetName) ? "Sheet1" : sheetName)
- .doWrite(data);
- } catch (IOException e) {
- log.error("导出excel文件异常", e);
- }
- }
-
- /**
- * 设置文件名
- *
- * @param response HttpServletResponse
- * @param fileName 文件名
- */
- private static void encodeFileName(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
- fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
- response.setCharacterEncoding(StandardCharsets.UTF_8.name());
- response.setHeader(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment;filename=\"%s\"", fileName + ".xlsx"));
- response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache");
- response.setHeader(HttpHeaders.PRAGMA, "no-cache");
- response.setDateHeader(HttpHeaders.EXPIRES, -1);
- }
-
- /**
- * 解析表头类中的下拉注解
- *
- * @param head 表头类
- * @return Map<下拉框列索引, 下拉框内容> map
- */
- private static <T> Map<Integer, ExcelSelected> resolveSelectedAnnotation(Class<T> head) {
- Map<Integer, ExcelSelected> selectedMap = new HashMap<>(16);
- Field[] fields = head.getDeclaredFields();
- for (int i = 0; i < fields.length; i++) {
- Field field = fields[i];
- ExcelSelected selected = field.getAnnotation(ExcelSelected.class);
- ExcelProperty property = field.getAnnotation(ExcelProperty.class);
- if (selected != null) {
- if (property != null && property.index() >= 0) {
- selectedMap.put(property.index(), selected);
- }
- }
- }
- return selectedMap;
- }
-
- /**
- * 为excel创建下拉框
- *
- * @param selectedMap 下拉框配置数据 Map<下拉框列索引, 下拉框内容>
- * @return intercepts handle sheet creation
- */
- private static SheetWriteHandler selectedSheetWriteHandler(Map<Integer, ExcelSelected> selectedMap) {
- return new SheetWriteHandler() {
- @Override
- public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
-
- }
-
- @Override
- public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
- //获取正在处理的工作表对象
- Sheet sheet = writeSheetHolder.getSheet();
- //获得一个用于创建数据验证规则的辅助对象,用于创建和配置数据验证规则
- DataValidationHelper helper = sheet.getDataValidationHelper();
- selectedMap.forEach((k, v) -> {//其中 (k, v) 表示 Map 的键和值,在每次迭代时会传入这两个参数。
- // 获取固定下拉框的内容
- List<String> source = new ArrayList<>();
- //如果 ExcelSelected 注解中的 source 数组不为空,则将其转换为 List,并添加到 source 变量中
- if (v.source().length > 0) {
- source.addAll(Arrays.asList(v.source()));
- }
-
- //CollUtil.isNotEmpty(source) 是一个对集合 source 进行非空判断的工具方法。
- if (CollUtil.isNotEmpty(source)) {
- /**
- * 代码创建一个 CellRangeAddressList 对象 rangeList,用于定义下拉框的范围。
- * 其中,v.firstRow() 和 v.lastRow() 分别表示下拉框的起始行和结束行,k 表示下拉框所在的列
- */
- CellRangeAddressList rangeList = new CellRangeAddressList(v.firstRow(), v.lastRow(), k, k);
- //用于定义下拉框的约束。该约束指定下拉框的选项为 source 列表中的内容
- DataValidationConstraint constraint = helper.createExplicitListConstraint(source.toArray(new String[0]));
- DataValidation validation = helper.createValidation(constraint, rangeList);
- //代码设置数据验证规则的错误样式为停止(DataValidation.ErrorStyle.STOP)
- validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
- //显示错误提示框(validation.setShowErrorBox(true))
- validation.setShowErrorBox(true);
- //隐藏下拉箭头(validation.setSuppressDropDownArrow(true))
- validation.setSuppressDropDownArrow(true);
- validation.createErrorBox("提示", "请输入下拉选项中的内容");
- sheet.addValidationData(validation);
- }
- });
- }
- };
- }
- }
(3)controller层
- @GetMapping("/export")
- public void exportData(@RequestParam(required = false) String deptId,//查询条件可不存在
- HttpServletResponse response) {
- // Data
- QueryWrapper<EmpUserEntity> wrapper = new QueryWrapper<>();
- wrapper.like(StrUtil.isNotBlank(deptId), "dept_id", deptId);
-
- //将部门id变为部门名称
- List<EmpUserEntity> list = empUserService.list(wrapper);
-
- EasyExcelUtils.writeExcelBySelect(response, "测试", "用户信息表", list);
- }
2、前端代码
(1)按钮展示
- <el-form-item style="float:right">
- <el-button type="success" @click="exportUser()">导出</el-button>
- </el-form-item>
- <el-form-item style="float:right">
- <el-input v-model="dataForm.deptId" placeholder="请选择部门进行导出" style="width: 200px" clearable></el-input>
- </el-form-item>
(2)方法定义
- // 导出用户,通过blob
- exportUser() {
- this.$axios({
- method: 'get',
- url: this.$http.adornUrl('/emp/user/export?deptId='+this.dataForm.deptId),
- responseType: 'blob',
- }).then(function (response){
- let blob = new Blob([response.data], { type: 'application/vnd.ms-excel;charset=utf-8' })
- let downloadElement = document.createElement('a');//创建一个 <a> 元素,用于执行文件下载操作。
- let href = window.URL.createObjectURL(blob); //创建下载的链接
- downloadElement.href = href;// 将下载链接指定给 <a> 元素的 href 属性。
- downloadElement.download = 'test.xlsx'; //下载后文件名
- document.body.appendChild(downloadElement);// 将 <a> 元素添加到页面的 <body> 元素中。
- downloadElement.click(); //点击下载
- document.body.removeChild(downloadElement); //下载完成移除元素
- window.URL.revokeObjectURL(href); //释放掉blob对象
- }).catch(function(error){
- this.$message.error(error)
- })
- },
3、实物展示
----------------------------------------------------------------------------
本页面的导出下拉框,是固定写入,若需要动态导入,可参考以下大佬文章:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。