From 6ef573c77708b83262c80e71eb63363eff709720 Mon Sep 17 00:00:00 2001 From: AntonyCheng <1911261716@qq.com> Date: Tue, 9 Apr 2024 23:18:17 +0800 Subject: [PATCH] =?UTF-8?q?SpringBoot=E5=88=9D=E5=A7=8B=E5=8C=96=E6=A8=A1?= =?UTF-8?q?=E6=9D=BFv2.1.4=20=E8=AE=BE=E8=AE=A1Apache=20POI=E6=93=8D?= =?UTF-8?q?=E4=BD=9CWord=E6=A8=A1=E5=9D=97=E5=8F=8A=E5=85=B6=E9=85=8D?= =?UTF-8?q?=E5=A5=97=E5=B7=A5=E5=85=B7=E7=B1=BB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 19 ++--- .../controller/AuthController.java | 10 +++ .../utils/document/excel/ExcelUtils.java | 21 +++--- .../utils/document/word/WordUtils.java | 74 +++++++++++++++++++ src/main/resources/templates/word/README.md | 1 + 5 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 src/main/resources/templates/word/README.md diff --git a/pom.xml b/pom.xml index 2e7a9227..31d6d9c0 100644 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ 8.5.9 5.2.5 + 1.12.2 3.3.4 8.0.3 @@ -393,6 +394,11 @@ poi-ooxml ${poi.version} + + com.deepoove + poi-tl + ${poi.tl.version} + com.alibaba easyexcel @@ -513,23 +519,10 @@ src/main/resources - - false src/main/webapp/ - - src/main/resources - - - application* - bootstrap* - logback* - - - true - diff --git a/src/main/java/top/sharehome/springbootinittemplate/controller/AuthController.java b/src/main/java/top/sharehome/springbootinittemplate/controller/AuthController.java index 4d76f501..24b0bd82 100644 --- a/src/main/java/top/sharehome/springbootinittemplate/controller/AuthController.java +++ b/src/main/java/top/sharehome/springbootinittemplate/controller/AuthController.java @@ -1,6 +1,7 @@ package top.sharehome.springbootinittemplate.controller; import cn.dev33.satoken.annotation.SaCheckLogin; +import cn.dev33.satoken.annotation.SaIgnore; import org.apache.commons.lang3.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -13,9 +14,11 @@ import top.sharehome.springbootinittemplate.model.dto.auth.AuthRegisterDto; import top.sharehome.springbootinittemplate.model.vo.auth.AuthLoginVo; import top.sharehome.springbootinittemplate.service.AuthService; +import top.sharehome.springbootinittemplate.utils.document.excel.ExcelUtils; import top.sharehome.springbootinittemplate.utils.satoken.LoginUtils; import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; import java.util.Map; /** @@ -31,6 +34,13 @@ public class AuthController { @Resource private AuthService authService; + @GetMapping("/test") + @SaIgnore + public R test(HttpServletResponse response) { + ExcelUtils.exportTemplateHttpServletResponse("test", response); + return R.empty(); + } + /** * 注册 * todo 模板默认不使用该接口,但保留该接口,因为该模板中真实增加用户的接口应该是管理员增加用户 diff --git a/src/main/java/top/sharehome/springbootinittemplate/utils/document/excel/ExcelUtils.java b/src/main/java/top/sharehome/springbootinittemplate/utils/document/excel/ExcelUtils.java index 61066c13..55b15a5a 100644 --- a/src/main/java/top/sharehome/springbootinittemplate/utils/document/excel/ExcelUtils.java +++ b/src/main/java/top/sharehome/springbootinittemplate/utils/document/excel/ExcelUtils.java @@ -3,9 +3,12 @@ import com.alibaba.excel.EasyExcel; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.core.io.ClassPathResource; import org.springframework.util.ResourceUtils; import top.sharehome.springbootinittemplate.common.base.ReturnCode; import top.sharehome.springbootinittemplate.config.easyexcel.convert.date.ExcelDateConverter; @@ -712,29 +715,27 @@ public static void exportTemplateHttpServletResponse(String templateName, Cl } /** - * 导出Excel模板目录下的模板文件,同时关闭请求响应流并提交响应,模板目录一定是resources文件夹下templates/excel目录 + * 导出Excel.xlsx模板目录下的模板文件,同时关闭请求响应流并提交响应,模板目录一定是resources文件夹下templates/excel目录 * 在Controller中建议直接返回void,如果想要统一返回响应类型R,可以使用R.empty()方法。 * * @param templateName resource模板名称(不带后缀) * @param response 请求响应流 - * @param 泛型T */ - public static void exportTemplateHttpServletResponse(String templateName, HttpServletResponse response) { + public static void exportTemplateHttpServletResponse(String templateName, HttpServletResponse response) { try { - File file = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "templates/excel/" + templateName + ExcelTypeEnum.XLSX.getValue()); - if (!file.isFile()) { - throw new FileNotFoundException(); + ClassPathResource classPathResource = new ClassPathResource("templates/excel/" + templateName + ExcelTypeEnum.XLSX.getValue()); + if (!classPathResource.exists()) { + throw new CustomizeExcelException(ReturnCode.EXCEL_FILE_ERROR, "模板文件[" + templateName + ".xlsx]未找到"); } + InputStream inputStream = classPathResource.getInputStream(); handleResponse(templateName, response); - FileInputStream fileInputStream = new FileInputStream(file); int len = 0; byte[] buffer = new byte[1024]; ServletOutputStream outputStream = response.getOutputStream(); - while ((len = fileInputStream.read(buffer)) > 0) { + while ((len = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, len); } - fileInputStream.close(); - outputStream.flush(); + inputStream.close(); outputStream.close(); } catch (FileNotFoundException e) { throw new CustomizeExcelException(ReturnCode.EXCEL_FILE_ERROR, "模板文件[" + templateName + ".xlsx]未找到"); diff --git a/src/main/java/top/sharehome/springbootinittemplate/utils/document/word/WordUtils.java b/src/main/java/top/sharehome/springbootinittemplate/utils/document/word/WordUtils.java index 63998ca7..ce17e9bf 100644 --- a/src/main/java/top/sharehome/springbootinittemplate/utils/document/word/WordUtils.java +++ b/src/main/java/top/sharehome/springbootinittemplate/utils/document/word/WordUtils.java @@ -2,6 +2,7 @@ import com.alibaba.excel.EasyExcel; import com.alibaba.excel.support.ExcelTypeEnum; +import com.deepoove.poi.XWPFTemplate; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import lombok.Builder; @@ -16,7 +17,9 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.util.Units; import org.apache.poi.xwpf.usermodel.*; +import org.springframework.core.io.ClassPathResource; import top.sharehome.springbootinittemplate.common.base.ReturnCode; +import top.sharehome.springbootinittemplate.exception.customize.CustomizeExcelException; import top.sharehome.springbootinittemplate.exception.customize.CustomizeReturnException; import javax.imageio.ImageIO; @@ -38,6 +41,77 @@ @Slf4j public class WordUtils { + /** + * 输出Word模板内部类 + * 该导出模板是基于POI-TL框架的封装,该框架主要以"{{XXX}}"型标签为内容插入点,具体用法查看https://deepoove.com/poi-tl + */ + public static class Template { + + /** + * 导出Word.docx模板目录下的模板文件到响应流,模板目录一定是resources文件夹下templates/word目录 + * + * @param templateName 模板名称 + * @param tagMap 标签Map + * @param filename 导出文件名称 + * @param response 响应流 + */ + public static void export(String templateName, Map tagMap, String filename, HttpServletResponse response) { + try { + handleWordResponse(filename, response); + ServletOutputStream outputStream = response.getOutputStream(); + export(templateName, tagMap, outputStream); + } catch (IOException e) { + throw new CustomizeReturnException(ReturnCode.WORD_FILE_ERROR); + } + } + + /** + * 导出Word.docx模板目录下的模板文件到输出流,模板目录一定是resources文件夹下templates/word目录 + * + * @param templateName 模板名称 + * @param tagMap 标签Map + * @param outputStream 输出流 + */ + public static void export(String templateName, Map tagMap, OutputStream outputStream) { + try { + ClassPathResource classPathResource = new ClassPathResource("templates/word/" + templateName + ".docx"); + if (!classPathResource.exists()) { + throw new CustomizeExcelException(ReturnCode.EXCEL_FILE_ERROR, "模板文件[" + templateName + ".docx]未找到"); + } + InputStream inputStream = classPathResource.getInputStream(); + XWPFTemplate template = XWPFTemplate.compile(inputStream).render(tagMap); + template.writeAndClose(outputStream); + inputStream.close(); + } catch (IOException e) { + throw new CustomizeReturnException(ReturnCode.WORD_FILE_ERROR); + } + } + + /** + * 处理ContentType是Word格式的响应 + * + * @param fileName 文件名 + * @param response 响应 + */ + private static void handleWordResponse(String fileName, HttpServletResponse response) throws UnsupportedEncodingException { + String realName = null; + if (StringUtils.isBlank(fileName)) { + realName = UUID.randomUUID().toString().replace("-", "") + ".docx"; + } else { + realName = UUID.randomUUID().toString().replace("-", "") + "_" + fileName + ".docx"; + } + String encodeName = URLEncoder + .encode(realName, StandardCharsets.UTF_8.toString()) + .replaceAll("\\+", "%20"); + String contentDispositionValue = "attachment; filename=" + encodeName + ";filename*=utf-8''" + encodeName; + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue); + response.setHeader("download-filename", encodeName); + response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=UTF-8"); + } + + } + /** * 写入Word数据内部类 */ diff --git a/src/main/resources/templates/word/README.md b/src/main/resources/templates/word/README.md new file mode 100644 index 00000000..037a2858 --- /dev/null +++ b/src/main/resources/templates/word/README.md @@ -0,0 +1 @@ +### 存放Word模板的目录 \ No newline at end of file