Java中使用EasyExcel

news2025/2/21 8:06:06

Java中使用EasyExcel

文章目录

  • Java中使用EasyExcel
    • 一:EasyExcel介绍
      • 1.1、核心函数
        • 导入数据
        • 导出数据
      • 1.2、项目实际应用
        • 导入数据
        • 导出数据
      • 1.3、相关注解
        • @ExcelProperty
        • 作用
        • 示例
    • 二:EasyExcel使用
      • 2.1、导入功能
      • 2.2、导出功能
    • 三:EasyExcel完整代码,异步导入导出+任务管理
        • 3.1、异步导入+记录导入失败文件导出+模版检查+模版参数检查
          • 1、controller层:
          • 2、serviceImpl层:
          • 3、枚举方法
          • 4.检查模板Listener
        • 3.2、下载导出文件
        • 3.3、异常情况处理
          • 1、字符库缺失:Mac或者Windows正常,Linux报错

一:EasyExcel介绍

  • EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。它能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能,主体由阿里团队封装并维护。
  • 本文示例所呈现的主体功能为导入,其中涵盖导入过程中自动记录解析的数据错误,并以 excel 的形式返回给前端,以及 excel 模板校验错误等情况;
<!-- easyexcel -->
<dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>easyexcel</artifactId>
     <version>4.0.3</version>
</dependency>

对于简单使用:EasyExcel简单Demo,查看基础Demo示例

官网链接:EasyExcel官网

1.1、核心函数

导入数据
String filePath = "C:\\Users\\fkjas\\Desktop\\lixl\\test.xlsx";
EasyExcel.read(filePath, User.class, new ExcelListener()).sheet().doRead();
导出数据
String filePath = "C:\\Users\\fkjas\\Desktop\\lixl\\test.xlsx";
EasyExcel.write(filePath, User.class).sheet("学生信息表").doWrite(list);

1.2、项目实际应用

导入数据
List<User> userList = EasyExcel.read(newBufferedInputStream(file.getInputStream())).head(User.class).sheet().doReadSync();
导出数据
EasyExcel.write(httpServletResponse.getOutputStream(), User.class).sheet("用户信息").doWrite(userList);

1.3、相关注解

@ExcelProperty
作用

标注列名以及各列的前后顺序

示例
@ExcelProperty(index = 0, value = "姓名")
private String userName;

二:EasyExcel使用

2.1、导入功能

思路:读取文件-》创建ExcelListener-》在Listener中封装处理数据-》批量处理

    /**
     * 文件上传
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link UploadData}
     * <p>
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}
     * <p>
     * 3. 直接读即可
     */
    @PostMapping("upload")
    @ResponseBody
    public String upload(MultipartFile file) throws IOException {
        EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
        return "success";
    }

2.2、导出功能

思路1:创建导出数据集-》调用EasyExcel的能力-》批量处理–》将结果传递到浏览器请求中,直接响应到浏览器下载

   /**
     * 文件下载(失败了会返回一个有部分数据的Excel)
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link DownloadData}
     * <p>
     * 2. 设置返回的 参数
     * <p>
     * 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
     */
    @GetMapping("download")
    public void download(HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
    }

思路2:创建导出数据集-》调用EasyExcel的能力-》批量处理–》将结果保存成为一个文件,记录任务,通过接口调用任务id进行下载文件

										//5.开始保存错误日志,将错误信息导出excel表格
                    //本地文件全路径
                    String fileExportPath = fileFolderPath + File.separator + taskCodeEnum.getDesc() + "-失败-" + DateUtil.getUniqueIdentifier() + "." + fileNamePrefix;
                    try (ExcelWriter excelWriter = EasyExcel.write(fileExportPath, WarehouseGoodsPriceErrorWriteReq.class).build()) {
                        // 这里注意 如果同一个sheet只要创建一次
                        WriteSheet writeSheet = EasyExcel.writerSheet("失败数据1").build();
                        excelWriter.write(failList, writeSheet);
                    } catch (Exception e) {
                        //如果Linux中出现null,https://easyexcel.opensource.alibaba.com/qa/#%E6%88%91%E5%9C%A8%E6%9C%AC%E5%9C%B0%E5%8F%AF%E4%BB%A5%E5%8F%91%E5%B8%83%E5%88%B0%E7%BA%BF%E4%B8%8A%E7%8E%AF%E5%A2%83%E6%80%8E%E4%B9%88%E4%B8%8D%E5%8F%AF%E4%BB%A5%E4%BA%86
                        //Linux下执行:yum install dejavu-sans-fonts fontconfig -y  /或者重装: sudo yum reinstall fontconfig -y
                        //确认: find /usr -name libfontconfig.so.1
                        log.error("easyExcel:导出excel失败", e);
                        puTaskRepo.updateTaskStatus(taskId, ExcelHandlerStatusEnum.FAILED.getCode());
                    }

三:EasyExcel完整代码,异步导入导出+任务管理

3.1、异步导入+记录导入失败文件导出+模版检查+模版参数检查
1、controller层:
    @ApiOperation(value = "导入excel模版,根据模版code来创建任务")
    @Valid
    @PostMapping("/importFile")
    public Result<Long> importFile(@ApiParam(name = "file", value = "文件")  @RequestParam("file") MultipartFile file,
                                     @ApiParam(name = "templateCode", value = "模版templateCode")  @NotBlank(message = "模版templateCode不能为空") String templateCode) {
        Long taskId= excelFileService.importFile(file, templateCode);
        return Result.ok(taskId);
    }
2、serviceImpl层:
@Override
    public Long importFile(MultipartFile multipartFile, String templateCode) {
        Map<String, String> map = ExcelImportTaskEnum.getMap();
        Long currentUserId = UserContext.getCurrentUserId();
        if (ObjectUtil.isEmpty(map)) {
            throw new ServiceException("任务不存在");
        }
        if (ObjectUtil.isNotEmpty(map)) {
            if (!map.containsKey(templateCode)) {
                throw new ServiceException("不支持此类任务,请联系管理员");
            }
        }
        Template template = templateRepo.getByCode(templateCode);
        if (ObjectUtil.isEmpty(template)) {
            throw new ServiceException("模板不存在");
        }
        String originalFilename = multipartFile.getOriginalFilename();
        String fileNamePrefix = FileUtil.getFileNamePrefix(originalFilename);
        ExcelImportTaskEnum taskCodeEnum = ExcelImportTaskEnum.getEnum(template.getCode());
        Long taskId;
        switch (taskCodeEnum) {
            //批量仓库门店价格修改
            case WAREHOUSE_GOODS_PRICE:
                taskId = handlerImportWarehouseGoodsPrice(currentUserId, multipartFile, taskCodeEnum, fileNamePrefix, template);
                break;
            default:
                throw new ServiceException("不支持此类型模版code");
        }
        return taskId;
    }
    
    
    /**
     * 获取文件名只获取后缀
     *
     * @param fileName
     * @return
     */
    public static String getFileNamePrefix(String fileName) {
        String extName = fileName.substring(fileName.lastIndexOf(".") + 1);
        return ObjectUtil.isEmpty(extName) ? "" : extName;
    }
3、枚举方法
package com.kkd.web.admin.model.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.EnumSet;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @beLongProjecet: kkd-all
 * @beLongPackage: com.kkd.web.admin.model.enums
 * @author: xiaoxiangyuan
 * @createTime: 2024/08/23 15:05
 * @description:
 * @version: v1.0
 */
@Getter
@AllArgsConstructor
@Slf4j
public enum ExcelImportTaskEnum {

    //模版任务
    WAREHOUSE_GOODS_PRICE("warehouseGoodsPrice", "批量修改仓库商品价格"),

    ;
    private String code;
    private String desc;

    public static ExcelImportTaskEnum getEnum(String code) {
        for (ExcelImportTaskEnum thirdTypeEnum : ExcelImportTaskEnum.values()) {
            if (thirdTypeEnum.getCode().equals(code)) {
                return thirdTypeEnum;
            }
        }
        return null;
    }

    public static String getDesc(String code) {
        for (ExcelImportTaskEnum thirdTypeEnum : ExcelImportTaskEnum.values()) {
            if (thirdTypeEnum.getCode().equals(code)) {
                return thirdTypeEnum.getDesc();
            }
        }
        return null;
    }


    public static boolean checkCodeExist(String code) {
        for (ExcelImportTaskEnum thirdTypeEnum : ExcelImportTaskEnum.values()) {
            if (thirdTypeEnum.getCode().equals(code)) {
                return true;
            }
        }
        return false;
    }

    public static Map<String, String> getMap() {
        Map<String, String> map = EnumSet.allOf(ExcelImportTaskEnum.class).stream()
                .collect(Collectors.toMap(ExcelImportTaskEnum::getCode, ExcelImportTaskEnum::getDesc));
        return map;
    }

    public static void main(String[] args) {
        log.info("getMap()的值为:{}", getMap());
    }
}

/**
     * 批量仓库门店价格修改
     *
     * @param currentUserId
     * @param multipartFile
     * @param taskCodeEnum
     * @param fileNamePrefix
     * @param template
     */
    private Long handlerImportWarehouseGoodsPrice(Long currentUserId, MultipartFile multipartFile, ExcelImportTaskEnum taskCodeEnum, String fileNamePrefix, Template template) {
        //1.校验模版头信息
        try {
            CheckHeaderListener headerValidationListener = new CheckHeaderListener(WarehouseGoodsPriceReadReq.class);
            EasyExcel.read(new BufferedInputStream(multipartFile.getInputStream()), WarehouseGoodsPriceReadReq.class, headerValidationListener).headRowNumber(1).sheet().doRead();
        } catch (IOException e) {
            throw new ServiceException("导入文件失败,请稍后重试");
        }
        //2.将导入文件临时存入服务器
        String fileFolderPath = getFileFolderPath();
        FileUtil.createFolder(fileFolderPath);
        //本地文件全路径
        String fileImportPath = fileFolderPath + File.separator + taskCodeEnum.getDesc() + "-" + DateUtil.getUniqueIdentifier() + "." + fileNamePrefix;
        try {
            //将文件保存到指定路径
            multipartFile.transferTo(new File(fileImportPath));
        } catch (IOException e) {
            log.error("保存文件异常", e);
            throw new ServiceException("保存文件异常");
        }
        //3.创建任务
        PuTask puTask = new PuTask();
        puTask.setFunctionType(ExcelFunctionTypeEnum.IMPORT_FILE.getCode());
        puTask.setOrgId(UserContext.getCurrentOrgId());
        puTask.setOperatorId(currentUserId);
        puTask.setTemplateCode(template.getCode());
        puTask.setStatus(ExcelHandlerStatusEnum.IN_PROGRESS.getCode());
        puTaskRepo.save(puTask);
        Long taskId = puTask.getId();
        try {
            //4.提交异步任务
            CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
                puTaskRepo.updateTaskStatus(taskId, ExcelHandlerStatusEnum.IN_PROGRESS.getCode());
                WarehouseGoodsPriceListener listener = new WarehouseGoodsPriceListener();
                listener.setCurrentUserId(currentUserId);
                EasyExcel.read(fileImportPath, WarehouseGoodsPriceReadReq.class, listener).sheet().doRead();
                Integer successCount = listener.getSuccessCount();
                Integer allCount = listener.getAllCount();
                List<WarehouseGoodsPriceErrorWriteReq> failList = listener.getFailList();
                Integer errorCount = ObjectUtil.isEmpty(failList) ? 0 : failList.size();
                String handlerResult = "总条数:" + allCount + ",成功条数:" + successCount + ",失败条数:" + errorCount;
                puTask.setHandleResult(handlerResult);
                if (ObjectUtil.isNotEmpty(failList)) {
                    //5.开始保存错误日志,将错误信息导出excel表格
                    //本地文件全路径
                    String fileExportPath = fileFolderPath + File.separator + taskCodeEnum.getDesc() + "-失败-" + DateUtil.getUniqueIdentifier() + "." + fileNamePrefix;
                    try (ExcelWriter excelWriter = EasyExcel.write(fileExportPath, WarehouseGoodsPriceErrorWriteReq.class).build()) {
                        // 这里注意 如果同一个sheet只要创建一次
                        WriteSheet writeSheet = EasyExcel.writerSheet("失败数据1").build();
                        excelWriter.write(failList, writeSheet);
                    } catch (Exception e) {
                        //如果Linux中出现null,https://easyexcel.opensource.alibaba.com/qa/#%E6%88%91%E5%9C%A8%E6%9C%AC%E5%9C%B0%E5%8F%AF%E4%BB%A5%E5%8F%91%E5%B8%83%E5%88%B0%E7%BA%BF%E4%B8%8A%E7%8E%AF%E5%A2%83%E6%80%8E%E4%B9%88%E4%B8%8D%E5%8F%AF%E4%BB%A5%E4%BA%86
                        //Linux下执行:yum install dejavu-sans-fonts fontconfig -y  /或者重装: sudo yum reinstall fontconfig -y
                        //确认: find /usr -name libfontconfig.so.1
                        log.error("easyExcel:导出excel失败", e);
                        puTaskRepo.updateTaskStatus(taskId, ExcelHandlerStatusEnum.FAILED.getCode());
                    }
                    puTask.setErrorData(JSON.toJSONString(failList));
                    puTask.setErrorUrl(fileExportPath);
                }
                puTaskRepo.updateById(puTask);
            }, poolExecutor);
            //6.同步数据库状态,删除临时文件
            completableFuture.thenRun(() -> {
                //修改状态已完成
                puTaskRepo.updateTaskStatus(taskId, ExcelHandlerStatusEnum.COMPLETED.getCode());
                //删除临时文件
                FileUtil.deleteTempFile(new File(fileImportPath));
            });
        } catch (Exception e) {
            log.error("处理失败,taskId:{},失败原因:{}", taskId, e.getMessage(), e);
            puTaskRepo.updateTaskStatus(taskId, ExcelHandlerStatusEnum.FAILED.getCode());
        }
        return taskId;
    }
4.检查模板Listener
package com.kkd.web.admin.model.excel.base;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.kkd.web.admin.model.util.EasyExcelUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

@Slf4j
public class CheckHeaderListener extends AnalysisEventListener<Object> {
    private final Class<?> clazz;
    private boolean position = true;

    public CheckHeaderListener(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        EasyExcelUtil.checkExcelHeader(headMap, clazz);
        position = false;
    }

    @Override
    public void invoke(Object data, AnalysisContext context) {

    }

    /**
     * 通过该方法控制是否继续解析
     * @param context
     * @return
     */
    @Override
    public boolean hasNext(AnalysisContext context) {
        return position;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
    }
}
package com.kkd.web.admin.model.excel.warehouse.goodsPrice.read.req;

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

/**
 * @beLongProjecet: kkd-all
 * @beLongPackage: com.kkd.web.admin.test.excel
 * @author: xiaoxiangyuan
 * @createTime: 2024/12/17 17:28
 * @description:
 * @version: v1.0
 */
@Data
public class WarehouseGoodsPriceReadReq {
    @ExcelProperty(index = 0,value = "*组织ID")
    private String orgId;

    @ExcelProperty(index = 1,value = "*SKU编码")
    private String sku;

    @ExcelProperty(index = 2,value = "*门店编码")
    private String qnhStoreId;

    @ExcelProperty(index = 3,value = "*商品零售价(元)")
    private String onLinePrice;
}

    /**
     * 创建文件夹
     *
     * @param path
     */
    public static void createFolder(String path) {
        File folder = new File(path);
        if (!folder.exists()) {
            folder.mkdirs();
        }
    }
    
    
        /**
     * 获取存储文件夹路径
     *
     * @return
     */
    private String getFileFolderPath() {
        //获取本地文件夹地址
        String savePath = null;
        if (SystemUtil.SystemNameEnum.TYPE1.getDesc().equals(SystemUtil.getSystemName())) {
            savePath = windowsPath;
        } else if (SystemUtil.SystemNameEnum.TYPE2.getDesc().equals(SystemUtil.getSystemName())) {
            savePath = linuxPath;
        } else {
            savePath = macPath;
        }
        return savePath;
    }

package com.kkd.common.model.util;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * @beLongProjecet: ai_ability_gateway
 * @beLongPackage: com.wondertek.util
 * @author: xiaoxiangyuan
 * @createTime: 2022/09/26 9:53
 * @description:
 * @version: v1.0
 */
@Slf4j
public class SystemUtil {

    /**
     * desc: 获取当前运行系统名称
     *
     * @param
     * @return
     */
    public static String getSystemName() {
        String os = System.getProperty("os.name");
        //Windows操作系统
        if (os != null && os.toLowerCase().startsWith("windows")) {
            log.debug("当前系统版本是:{}", os);
            return SystemNameEnum.TYPE1.getDesc();
        } else if (os != null && os.toLowerCase().startsWith("linux")) {//Linux操作系统
            log.debug("当前系统版本是:{}", os);
            return SystemNameEnum.TYPE2.getDesc();
        } else { //其它操作系统
            log.debug("当前系统版本是:{}", os);
            return SystemNameEnum.TYPE3.getDesc();
        }

    }


    @AllArgsConstructor
    @Getter
    public enum SystemNameEnum {
        //
        TYPE1("windows"),
        TYPE2("linux"),
        TYPE3("mac");
        private String desc;

    }
}

package com.kkd.web.admin.model.excel.warehouse.goodsPrice.write.req;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.alibaba.excel.annotation.write.style.HeadStyle;
import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
import com.kkd.web.admin.model.excel.warehouse.goodsPrice.read.req.WarehouseGoodsPriceReadReq;
import lombok.Data;

/**
 * @beLongProjecet: kkd-all
 * @beLongPackage: com.kkd.web.admin.test.excel
 * @author: xiaoxiangyuan
 * @createTime: 2024/12/17 17:28
 * @description:
 * @version: v1.0
 */
@Data
@HeadRowHeight(35)
@ColumnWidth(30)
@ContentRowHeight(20)
public class WarehouseGoodsPriceErrorWriteReq extends WarehouseGoodsPriceReadReq {

    @ExcelProperty(index = 4,value = "失败原因")
    @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10)
    private String errorMsg;

}

    /**
     * 删除本地临时文件
     *
     * @param file :删除file对象
     */
    public static void deleteTempFile(File file) {
        if (file != null) {
            File del = new File(file.toURI());
            del.delete();
        }
    }
CREATE TABLE `pu_task` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `org_id` bigint NOT NULL DEFAULT '0' COMMENT '组织id',
  `function_type` tinyint(1) NOT NULL DEFAULT '0' COMMENT '功能 0:导入  1:导出',
  `operator_id` bigint NOT NULL COMMENT '操作人',
  `template_code` varchar(64) NOT NULL COMMENT '模版code',
  `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '执行状态  0:未执行  1:执行中   2:执行完成   3:执行失败',
  `handle_result` varchar(64) NOT NULL DEFAULT '' COMMENT '执行结果',
  `success_url` varchar(255) DEFAULT '' COMMENT '处理导出成功数据文件路径',
  `error_url` varchar(255) DEFAULT '' COMMENT '处理失败数据文件路径',
  `error_data` text COMMENT '处理失败数据',
  `redundancy` varchar(255) DEFAULT NULL COMMENT '冗余字段',
  `is_delete` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态  0:未删除  1:已删除',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  KEY `idx_org_id` (`org_id`),
  KEY `idx_template_code` (`template_code`),
  KEY `idx_operator_id` (`operator_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='导入导出任务表';
CREATE TABLE `pu_template` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL DEFAULT '' COMMENT '模版名称',
  `code` varchar(64) NOT NULL DEFAULT '' COMMENT '模版code',
  `url` varchar(255) NOT NULL DEFAULT '' COMMENT '模板链接',
  `is_delete` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除状态  0:未删除  1:已删除',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  `create_user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建者',
  `update_user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '更新者',
  PRIMARY KEY (`id`),
  KEY `idx_code` (`code`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='导入导出模板管理';
3.2、下载导出文件
@ApiOperation(value = "下载处理异常的excel,根据任务id")
@Valid
@GetMapping("/downloadFile")
public Result<String> downloadFile(HttpServletResponse httpServletResponse,
                                 @ApiParam(name = "taskId", value = "任务ID")  @NotNull(message = "任务id不能为空") Long taskId,
                                 @ApiParam(name = "functionType", value = "功能 0:导入  1:导出")  @NotNull(message = "任务类型functionType不能为空") @Min(value = 0,message = "最小为0") @Max(value = 1,message = "最大为1")Integer functionType,
                                 @ApiParam(name = "downloadType", value = "下载成功/失败文件,导入类型只有失败文件  0:成功  1:失败") @NotNull(message = "下载类型downloadType不能为空") @Min(value = 0,message = "最小为0") @Max(value = 1,message = "最大为1")Integer downloadType

) {
    excelFileService.downloadFile(httpServletResponse, taskId,functionType,downloadType);
    return Result.ok();
}
@Override
public void downloadFile(HttpServletResponse httpServletResponse, Long taskId, Integer functionType, Integer downloadType) {
    PuTask puTask = puTaskRepo.getById(taskId);
    if (ObjectUtil.isEmpty(puTask)) {
        throw new ServiceException("任务不存在");
    }
    String path = getFileUrl(functionType, downloadType, puTask);
    String fileName = Paths.get(path).getFileName().toString();
    try {
        // 将数据用户信息导出成Excel文件并以流的形式返回
        httpServletResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        // 设置字符集
        httpServletResponse.setCharacterEncoding("utf-8");
        // 设置文件名,并且进行编码
        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        // 告诉浏览器将以下载的方式处理响应,而不是在浏览器中直接打开(attachment表示下载、filename*=utf-8''" + fileName + ".xlsx"表示下载的文件名)
        httpServletResponse.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
        File file = new File(path);
        if (!file.exists()) {
            throw new ServiceException("文件不存在:" + path);
        }
        // 将文件内容写入到响应流中
        try (InputStream inputStream = new FileInputStream(file); OutputStream outputStream = httpServletResponse.getOutputStream()) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.flush();
        }
    } catch (Exception e) {
        throw new ServiceException("导出文件失败,请联系管理员");
    }
}
3.3、异常情况处理
1、字符库缺失:Mac或者Windows正常,Linux报错
java.lang.NullPointerException: null
        at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)
        at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219)
        at sun.awt.FontConfiguration.init(FontConfiguration.java:107)
        at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774)
        at sun.font.SunFontManager$2.run(SunFontManager.java:441)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.font.SunFontManager.<init>(SunFontManager.java:386)
        at sun.awt.FcFontManager.<init>(FcFontManager.java:35)
        at sun.awt.X11FontManager.<init>(X11FontManager.java:57)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at java.lang.Class.newInstance(Class.java:442)
        at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74)
        at java.awt.Font.getFont2D(Font.java:491)
        at java.awt.Font.canDisplayUpTo(Font.java:2064)
        at java.awt.font.TextLayout.singleFont(TextLayout.java:470)
        at java.awt.font.TextLayout.<init>(TextLayout.java:531)
        at org.apache.poi.ss.util.SheetUtil.getDefaultCharWidthAsFloat(SheetUtil.java:352)
        at org.apache.poi.xssf.streaming.AutoSizeColumnTracker.<init>(AutoSizeColumnTracker.java:117)
        at org.apache.poi.xssf.streaming.SXSSFSheet.<init>(SXSSFSheet.java:106)
        at org.apache.poi.xssf.streaming.SXSSFWorkbook.createAndRegisterSXSSFSheet(SXSSFWorkbook.java:694)
        at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:712)
        at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:104)
        at com.alibaba.excel.util.WorkBookUtil.createSheet(WorkBookUtil.java:86)
        at com.alibaba.excel.context.WriteContextImpl.createSheet(WriteContextImpl.java:223)
        at com.alibaba.excel.context.WriteContextImpl.initSheet(WriteContextImpl.java:203)
        at com.alibaba.excel.context.WriteContextImpl.currentSheet(WriteContextImpl.java:135)
        at com.alibaba.excel.write.ExcelBuilderImpl.addContent(ExcelBuilderImpl.java:54)
        at com.alibaba.excel.ExcelWriter.write(ExcelWriter.java:73)
        at com.alibaba.excel.ExcelWriter.write(ExcelWriter.java:50)
        at com.kkd.web.admin.service.impl.ExcelFileServiceImpl.handlerExcelWarehouseGoodsPrice(ExcelFileServiceImpl.java:209)
        at com.kkd.web.admin.service.impl.ExcelFileServiceImpl.importFile(ExcelFileServiceImpl.java:142)
        at com.kkd.web.admin.controller.excel.ExcelFileController.importFile(ExcelFileController.java:43)
        at com.kkd.web.admin.controller.excel.ExcelFileController$$FastClassBySpringCGLIB$$1c09eef6.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
        at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:123)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708)
        at com.kkd.web.admin.controller.excel.ExcelFileController$$EnhancerBySpringCGLIB$$5472f61d.importFile(<generated>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:696)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:779)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1789)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:750)

文档:EasyExcel官方解释

image-20241226162206328

异常原因:Linux上安装的jdk是openjdk,其中字体库缺失导致

方法一:更换jdk版本,将openjdk更换为sun下的jdk即可解决
方法二:Linux下执行:yum install dejavu-sans-fonts fontconfig -y  
   		或者重装: sudo yum reinstall fontconfig -y
   		确认: find /usr -name libfontconfig.so.1

最后:重新启动项目即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2298986.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

建筑兔零基础自学python记录18|实战人脸识别项目——视频检测07

本次要学视频检测&#xff0c;我们先回顾一下图片的人脸检测建筑兔零基础自学python记录16|实战人脸识别项目——人脸检测05-CSDN博客 我们先把上文中代码复制出来&#xff0c;保留红框的部分。 ​ 然后我们来看一下源代码&#xff1a; import cv2 as cvdef face_detect_demo(…

自定义基座实时采集uniapp日志

自定义基座实时采集uniapp日志 打测试包给远端现场(测试/客户)实际测试时也能实时看到日志了&#xff0c;也有代码行数显示。 流程设计 #mermaid-svg-1I5W9r1DU4xUsaTF {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid…

AI知识库 - Cherry Studio

1 引言&#xff1a; 最近 DeepSeek 很火啊&#xff0c;想必大家都知道&#xff0c;DeepSeek 这个开源的模型出来后&#xff0c;因其高质量能力和R1 的思维链引发了大家本地部署的热潮。我也不例外&#xff0c;本地部署了一个 14B 的模型&#xff0c;然后把&#xff0c;感觉傻傻…

20250213 隨筆 雪花算法

雪花算法&#xff08;Snowflake Algorithm&#xff09; 雪花算法&#xff08;Snowflake&#xff09; 是 Twitter 在 2010 年開發的一種 分布式唯一 ID 生成算法&#xff0c;它可以在 高併發場景下快速生成全局唯一的 64-bit 長整型 ID&#xff0c;且不依賴資料庫&#xff0c;具…

(前端基础)HTML(一)

前提 W3C:World Wide Web Consortium&#xff08;万维网联盟&#xff09; Web技术领域最权威和具有影响力的国际中立性技术标准机构 其中标准包括&#xff1a;机构化标准语言&#xff08;HTML、XML&#xff09; 表现标准语言&#xff08;CSS&#xff09; 行为标准&#xf…

pdf.js默认显示侧边栏和默认手形工具

文章目录 默认显示侧边栏(切换侧栏)默认手形工具(手型工具) 大部分的都是在viewer.mjs中的const defaultOptions 变量设置默认值,可以使用数字也可以使用他们对应的变量枚举值 默认显示侧边栏(切换侧栏) 在viewer.mjs中找到defaultOptions,大概在732行,或则搜索sidebarViewOn…

学习总结三十三

括号序列 如果它是一个右括号&#xff0c;考察它与它左侧离它最近的未匹配的的左括号。如果该括号与之对应&#xff08;即小括号匹配小括号&#xff0c;中括号匹配中括号&#xff09;&#xff0c;则将二者配对。简单理解&#xff0c;找到一个右括号&#xff0c;向左找一个左括号…

解决DeepSeek服务器繁忙问题

目录 解决DeepSeek服务器繁忙问题 一、用户端即时优化方案 二、高级技术方案 三、替代方案与平替工具&#xff08;最推荐简单好用&#xff09; 四、系统层建议与官方动态 用加速器本地部署DeepSeek 使用加速器本地部署DeepSeek的完整指南 一、核心原理与工具选择 二、…

Huatuo热更新--安装HybridCLR

1.自行安装unity编辑器 支持2019.4.x、2020.3.x、2021.3.x、2022.3.x 中任一版本。推荐安装2019.4.40、2020.3.26、2021.3.x、2022.3.x版本。 根据你打包的目标平台&#xff0c;安装过程中选择必要模块。如果打包Android或iOS&#xff0c;直接选择相应模块即可。如果你想打包…

flink cdc2.2.1同步postgresql表

目录 简要说明前置条件maven依赖样例代码 简要说明 在flink1.14.4 和 flink cdc2.2.1下&#xff0c;采用flink sql方式&#xff0c;postgresql同步表数据&#xff0c;本文采用的是上传jar包&#xff0c;利用flink REST api的方式进行sql执行。 前置条件 1.开启logical 确保你…

纪念日倒数日项目的实现-【纪念时刻-时光集】

纪念日/倒数日项目的实现## 一个练手的小项目&#xff0c;uniappnodemysql七牛云。 在如今快节奏的生活里&#xff0c;大家都忙忙碌碌&#xff0c;那些具有特殊意义的日子一不小心就容易被遗忘。今天&#xff0c;想给各位分享一个“纪念日”项目。 【纪念时刻-时光集】 一…

WPF的MVVMLight框架

在NuGet中引入该库&#xff1a; MVVMLight框架中的命令模式的使用&#xff1a; <StackPanel><TextBox Text"{Binding Name}"/><TextBox Text"{Binding Title}"/><Button Content"点我" Command"{Binding ShowCommand…

DeepSeek从入门到精通(清华大学)

​ DeepSeek是一款融合自然语言处理与深度学习技术的全能型AI助手&#xff0c;具备知识问答、数据分析、编程辅助、创意生成等多项核心能力。作为多模态智能系统&#xff0c;它不仅支持文本交互&#xff0c;还可处理文件、图像、代码等多种格式输入&#xff0c;其知识库更新至2…

【DeepSeek】DeepSeek R1 本地windows部署(Ollama+Docker+OpenWebUI)

1、背景&#xff1a; 2025年1月&#xff0c;DeepSeek 正式发布 DeepSeek-R1 推理大模型。DeepSeek-R1 因其成本价格低廉&#xff0c;性能卓越&#xff0c;在 AI 行业引起了广泛关注。DeepSeek 提供了多种使用方式&#xff0c;满足不同用户的需求和场景。本地部署在数据安全、性…

windows平台上 oracle简单操作手册

一 环境描述 Oracle 11g单机环境 二 基本操作 2.1 数据库的启动与停止 启动: C:\Users\Administrator>sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 Production on 星期五 7月 31 12:19:51 2020 Copyright (c) 1982, 2013, Oracle. All rights reserved. 连接到:…

【弹性计算】弹性计算的技术架构

弹性计算的技术架构 1.工作原理2.总体架构3.控制面4.数据面5.物理设施层 虽然弹性计算的产品种类越来越多&#xff0c;但不同产品的技术架构大同小异。下面以当前最主流的产品形态 —— 云服务器为例&#xff0c;探查其背后的技术秘密。 1.工作原理 云服务器通常以虚拟机的方…

RAG(检索增强生成)落地:基于阿里云opensearch视线智能问答机器人与企业知识库

文章目录 一、环境准备二、阿里云opensearch准备1、产品文档2、准备我们的数据3、上传文件 三、对接1、对接文本问答 一、环境准备 # 准备python环境 conda create -n opensearch conda activate opensearch# 安装必要的包 pip install alibabacloud_tea_util pip install ali…

【踩坑】pytorch模型导出部署onnx问题记录

问题1&#xff1a;repeat_interleave 无法转译 具体报错为&#xff1a; TypeError: torch._C.Value object is not iterable (Occurred when translating repeat_interleave).原因是我的模型代码中有&#xff1a; batch_indices torch.repeat_interleave(torch.arange(can…

DeepSeek vs ChatGPT:AI对决中的赢家是……人类吗?

DeepSeek vs ChatGPT&#xff1a;AI对决中的赢家是……人类吗&#xff1f; 文章目录 DeepSeek vs ChatGPT&#xff1a;AI对决中的赢家是……人类吗&#xff1f;一、引言1. 背景2. 问题 二、DeepSeek vs ChatGPT&#xff1a;谁更胜一筹&#xff1f;2.1 语言生成能力评测对比场景…

基于ollama搭建本地deepseek大模型服务

基于ollama搭建本地deepseek大模型服务 简介准备工作系统要求ollama的安装ollama 模型ollama 安装流程ollama 如何运行大模型前端部署注意事项简介 本指南旨在帮助初学者在本地环境中设置和运行DeepSeek大模型服务。本文将使用Ollama平台来简化这一过程,确保即使是新手也能顺…