一、引入easyexcel包

gradle中引入jar

dependencies {
    compile 'com.alibaba:easyexcel:1.0.1'
}

maven中引入jar

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

2、要上实现代码了

Controller层

@Api(tags = {"试卷Api"})
@RestController
@RequestMapping("api/v1/paper")
@Slf4j
public class PaperController {


    @Autowired
    private ExportPaperStrategyHandler handler;

    /**
     * 注:请不要格式化PaperWord.ftl文件,会格式化后会因为空格,导致图片导出失败
     */
    @ApiOperation("导出试卷excel或者word")
    @GetMapping("/export-paper")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "paperIds", value = "试卷id", dataType = "Integer", required = true),
            @ApiImplicitParam(name = "exportTypeEnum", value = "导出类型", dataType = "String", required = true)})
    public void exportPaperExcelOrWord(@RequestParam("paperIds") List<Integer> paperIds,
                                       @RequestParam("exportTypeEnum") String exportTypeEnum,
                                       HttpServletResponse response) throws IOException {
        String exportType = exportTypeEnum.toUpperCase().trim();
        ExportPaperProcessor processor = handler.getProcessor(ExportTypeEnum.valueOf(exportType));
        processor.exportPaperProcess(paperIds, ExportTypeEnum.valueOf(exportType), response);
    }
}

Handler层

@Service
public class ExportPaperStrategyHandler {

    @Autowired
    private final Map<String, ExportPaperProcessor> orderStrategyMap = new ConcurrentHashMap<>();

    public ExportPaperProcessor getProcessor(ExportTypeEnum typeEnum) {
        return orderStrategyMap.get(typeEnum.name());
    }

}

接口层

public interface ExportPaperProcessor {
    /**
     * 通过试卷id及导出类型,导出excel或word
     *
     * @param paperIds 试卷id集合
     * @param typeEnum 枚举
     * @param response 返回流
     * @throws IOException IO异常
     */
    void exportPaperProcess(List<Integer> paperIds, ExportTypeEnum typeEnum, HttpServletResponse response) throws IOException;

}

 导出Excel实现

@Component("EXCEL")
public class ExportPaperExcelProcessor implements ExportPaperProcessor {

    @Autowired
    private PaperService paperService;

    @Override
    public void exportPaperProcess(List<Integer> paperIds, ExportTypeEnum typeEnum, HttpServletResponse response) throws IOException {
        List<ExcelPropertyPaperModel> data = paperService.exportPaperExcel(paperIds);
        ExcelWriter writer = new ExcelWriter(PaperExcelWrite.getOutputStream("PaperExcel", response), ExcelTypeEnum.XLSX);
        Sheet sheetData = new Sheet(1, 0, ExcelPropertyPaperModel.class);
        sheetData.setSheetName("PaperExcel");
        writer.write(data, sheetData);
        writer.finish();
    }
}

导出Word实现

@Component("WORD")
public class ExportPaperWordProcessor implements ExportPaperProcessor {

    @Autowired
    private PaperService paperService;

    @Override
    public void exportPaperProcess(List<Integer> paperIds, ExportTypeEnum typeEnum, HttpServletResponse response) {
        List<ExcelPropertyPaperModel> excelPropertyPaperModels = paperService.exportPaperExcel(paperIds);
        List<ExcelPropertyPaperModel> list = new ArrayList<>();
        for (ExcelPropertyPaperModel excelPropertyPaperModel : excelPropertyPaperModels) {
            ExcelPropertyPaperModel model = new ExcelPropertyPaperModel();
            model.setPaperId(excelPropertyPaperModel.getPaperId());
            model.setItemId(excelPropertyPaperModel.getItemId());
            model.setType(NullCheck.get(excelPropertyPaperModel.getType()));
            model.setRank(NullCheck.get(excelPropertyPaperModel.getRank()));
            model.setTitle(Jsoup.parse(NullCheck.get(excelPropertyPaperModel.getTitle())).text());
            model.setAnswer(NullCheck.get(excelPropertyPaperModel.getAnswer()));
            model.setSelect(Jsoup.parse(NullCheck.get(excelPropertyPaperModel.getSelect())).text());
            model.setAnalysis(Jsoup.parse(NullCheck.get(excelPropertyPaperModel.getAnalysis())).text());
            model.setKnowledgePoint(NullCheck.get(excelPropertyPaperModel.getKnowledgePoint()));
            model.setImageTitles(getImageSrc(Jsoup.clean(NullCheck.get(excelPropertyPaperModel.getTitle()), Whitelist.relaxed())));
            model.setImageAnalysis(getImageSrc(Jsoup.clean(NullCheck.get(excelPropertyPaperModel.getAnalysis()), Whitelist.relaxed())));
            list.add(model);
        }
        ExportPaperWordUtil ewUtil = new ExportPaperWordUtil();
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("list", list);
        ewUtil.exportWordBatch(dataMap, "PaperWord", "PaperWord.ftl", response);

    }


    /**
     * 获取获取图片Base64字符串
     *
     * @param clean 题目标签字符串
     * @return 图片Base64字符串
     */
    public List<String> getImageSrc(String clean) {
        List<String> listSrc = new ArrayList<>();
        Document doc = Jsoup.parse(clean);
        // 获取带有src属性的img元素
        Elements imgTags = doc.select("img[src]");
        for (Element element : imgTags) {
            // 获取src的绝对路径
            String src = element.attr("src");
            // 根据src获取图片
            String imageBase64StrFromUrl = WebFileUtils.getImageBase64StrFromUrl(src);
            listSrc.add(imageBase64StrFromUrl != null ? imageBase64StrFromUrl : "");
        }
        return listSrc;
    }
}

wordUtil  压缩包的类

@Slf4j
public class ExportPaperWordUtil {
    private Configuration config;

    public ExportPaperWordUtil() {
        config = new Configuration(DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        config.setDefaultEncoding("utf-8");
        config.setOutputFormat(XMLOutputFormat.INSTANCE);
    }

    /**
     * 压缩包方式导出多个word
     * 由于一次请求浏览器只能响应一次,想导出多个必须打包,亲测for循环导出只能导一个
     * 如果想做到分别单独下载,那就得用插件啦,这里不提供插件的做法
     * 思路:生成临时目录-在临时目录生成word-将临时目录打zip包-zip文件下载-删除临时目录和zip包,
     * 回收系统资源
     */
    public void exportWordBatch(Map<String, Object> map, String title, String templateName,
                                HttpServletResponse response) {
        File zipFile = null;
        File directory = null;
        InputStream fin = null;
        ServletOutputStream out = null;
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/octet-stream");
        response.addHeader("Content-Disposition", "attachment;filename=" + System.currentTimeMillis() + ".zip");
        //加载模板(路径)数据,也可使用setServletContextForTemplateLoading()方法放入到web文件夹下
        config.setClassForTemplateLoading(this.getClass(), "/templates");
        //设置异常处理器 这样的话 即使没有属性也不会出错 如:${list.name}...不会报错
        config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        try {
            if (templateName.endsWith(ConstantConfig.PATH_TYPE)) {
                templateName = templateName.substring(0, templateName.indexOf(ConstantConfig.PATH_TYPE));
            }
            Template freemarkerTemplate = config.getTemplate(templateName + ConstantConfig.PATH_TYPE);
            out = response.getOutputStream();
            // 根据当前时间创建临时目录
            String path = this.getClass().getResource("/").getPath() + "resources/word/" + System.currentTimeMillis();
            directory = new File(path);
            directory = new File(path);
            directory.mkdirs();
            // 调用工具类的createDoc方法在临时目录下生成Word文档
            createDoc(map, freemarkerTemplate, directory.getPath() + "/" + title + ".doc");
            //压缩目录
            ZipUtil.createZip(path, path + "zip.zip");
            // 根据路径获取刚生成的zip包文件
            zipFile = new File(path + "zip.zip");
            fin = new FileInputStream(zipFile);
            // 缓冲区
            byte[] buffer = new byte[1024];
            int bytesToRead = -1;
            // 通过循环将读入的Word文件的内容输出到浏览器中
            while ((bytesToRead = fin.read(buffer)) != -1) {
                out.write(buffer, 0, bytesToRead);
            }
            response.flushBuffer();
        } catch (Exception e) {
            log.error("exportWordBatch", e);
        } finally {
            try {
                if (fin != null) {
                    fin.close();
                }
                if (out != null) {
                    out.close();
                }
                if (zipFile != null) {
                    zipFile.delete();
                }
                if (directory != null) {
                    //递归删除目录及目录下文件
                    ZipUtil.deleteFile(directory);
                }
            } catch (Exception e2) {
                log.error("exportWordBatch", e2);
            }

        }
    }

    /**
     * 生成word文档
     *
     * @param dataMap  参数
     * @param template 模版
     * @param filename 文件名称
     * @return 文件对象
     */
    private File createDoc(Map<?, ?> dataMap, Template template, String filename) {
        File file = new File(filename);
        Template templateData = template;
        Writer writer = null;
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            writer = new OutputStreamWriter(fos, UTF_8);
            templateData.process(dataMap, writer);
        } catch (Exception ex) {
            log.error("createDoc", ex);
        } finally {
            try {
                fos.close();
                writer.close();
            } catch (Exception e) {
                log.error("createDoc", e);
            }

        }
        return file;
    }

}

实体model,定义excel导出的表头

@EqualsAndHashCode(callSuper = true)
@Data
@JsonFilter("excelPropertyPaperModelFilter")
public class ExcelPropertyPaperModel extends BaseRowModel {
    private Paper paper;
    private Item item;
    private PaperItem paperItem;
    private List<ItemSelect> itemSelects;
    private List<String> knowledgePoints;
    @ExcelProperty(value = "试卷id", index = 0)
    private String paperId;
    @ExcelProperty(value = "题目id", index = 1)
    private String itemId;
    @ExcelProperty(value = "类型", index = 2)
    private String type;
    @ExcelProperty(value = "难度", index = 3)
    private String rank;
    @ExcelProperty(value = "题干", index = 4)
    private String title;
    @ExcelProperty(value = "正确答案", index = 5)
    private String answer;
    @ExcelProperty(value = "选项", index = 6)
    private String select;
    @ExcelProperty(value = "解析", index = 7)
    private String analysis;
    @ExcelProperty(value = "知识点标签", index = 8)
    private String knowledgePoint;
    @ExcelProperty(value = "题干图片", index = 9)
    private List<String> imageTitles;
    @ExcelProperty(value = "解析图片", index = 10)
    private List<String> imageAnalysis;

    public void fillInAll() {
        this.paperId = paper.getId().toString();
        this.itemId = item.getId().toString();
        this.type = ItemTypeEnum.getItemType(item.getType());
        this.rank = item.getRank() == 1 ? "难" : (item.getRank() == 2 ? "中" : "易");
        this.title = item.getTitle();
        this.answer = item.getAnswer();
        this.analysis = item.getAnalysis();
        this.select = StringUtils.join(getItemSelect(itemSelects), "/");
        this.knowledgePoint = StringUtils.join(knowledgePoints, "/");
    }

    public List<String> getItemSelect(List<ItemSelect> itemSelects) {
        List<String> list = new ArrayList<>();
        if (itemSelects.isEmpty()) {
            return list;
        }
        for (ItemSelect itemSelect : itemSelects) {
            String itemSelectData = itemSelect.getOption() + "、" + itemSelect.getItemOption();
            list.add(itemSelectData);
        }
        return list;
    }

}