package cn.keking.service; import cn.afterturn.easypoi.word.WordExportUtil; import cn.keking.config.ConfigConstants; import cn.keking.config.KkfileConfig; import cn.keking.exception.KkFileException; import cn.keking.service.impl.OtherFilePreviewImpl; import cn.keking.utils.Consts; import cn.keking.utils.WebUtils; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections4.MapUtils; import org.apache.commons.io.FilenameUtils; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge; import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.springframework.util.StreamUtils; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.util.HtmlUtils; import javax.annotation.Resource; import java.io.*; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; /** * @author Hikaru */ @Service @Slf4j public class ContractService { /** * 配置常量 */ @Resource private ConfigConstants configConstants; /** * kkfile配置 */ @Resource private KkfileConfig kkfileConfig; private final OfficeToPdfService officeToPdfService; public ContractService(FileHandlerService fileHandlerService, OfficeToPdfService officeToPdfService, OtherFilePreviewImpl otherFilePreview) { this.officeToPdfService = officeToPdfService; } /** * 上传 * * @param templateName 模板名 * @param multipartFile 模板文件 * @return 路径 * @throws IOException 异常 */ public String upload(String templateName, MultipartFile multipartFile) { if (multipartFile == null) { throw new KkFileException("上传文件不能为空"); } // 获取文件名 String fileName = multipartFile.getOriginalFilename(); fileName = HtmlUtils.htmlEscape(fileName, StandardCharsets.UTF_8.name()); //获取文件大小 ,文件大小不超过10m long size = multipartFile.getSize(); if (size > kkfileConfig.getTemplateMaxSize()) { long currentSizeM = kkfileConfig.getTemplateMaxSize() / 1024 / 1024; throw new KkFileException("上传的文件限制大小为" + currentSizeM + "m"); } //获取文件扩展名,目前模板只支持docx String extensionName = getExtension(fileName); if (!kkfileConfig.getAllowUploadType().contains(extensionName)) { throw new KkFileException("不支持的文件类型【" + extensionName + "】"); } //目录不存在重新生成目录 File outFile = new File(Consts.getTemplatePath()); if (!outFile.exists() && !outFile.mkdirs()) { log.error("创建文件夹【{}】失败,请检查目录权限!", Consts.getTemplatePath()); } String newFileName = templateName + "." + extensionName; String url = this.configConstants.getBaseUrl() + "/" + Consts.TEMPLATE_PATH_NAME + "/" + templateName + "." + extensionName; String filePath = Consts.getTemplatePath() + File.separator + newFileName; log.info("上传文件:{}", filePath); try (InputStream in = multipartFile.getInputStream(); OutputStream out = new FileOutputStream(filePath)) { StreamUtils.copy(in, out); } catch (Exception e) { log.error("文件拷贝失败:", e); throw new KkFileException("文件拷贝失败"); } return url; } /** * 查询所有模板url * * @param templateNames 模板名称 * @return 模板url */ public List listTemplate(List templateNames) { return templateNames.stream().map(s -> downUrl(s, "docx")).collect(Collectors.toList()); } private String downUrl(String templateName, String extensionName) { return this.configConstants.getBaseUrl() + "/" + Consts.TEMPLATE_PATH_NAME + "/" + templateName + "." + extensionName; } /** * 根据模板生成内容 * * @param templateName 模板名称 * @param dataMap 数据 * @return 生成得预览路径 * @throws Exception 异常 */ public String gen(String templateName, Map dataMap) throws Exception { OutputStream os = null; try { File outFile = new File(Consts.uploadAbsDir()); if (!outFile.exists()) { outFile.mkdirs(); } String templatePath = Consts.getTemplatePath(); String abTemplatePath = templatePath + File.separator + templateName + ".docx"; //判断模板是否存在 File file = new File(abTemplatePath); if (!file.exists()) { throw new KkFileException("请先上传模板!"); } XWPFDocument xwpfDocument = WordExportUtil.exportWord07(abTemplatePath, dataMap); //如果是签收单,则需要合并单元格 if (Consts.RECEIPT_FORM.equals(templateName)){ List invoiceResList = (List) MapUtils.getObject(dataMap, "invoiceResList"); mergeCell(xwpfDocument,0,7,7+invoiceResList.size()-1,Arrays.asList(5,6)); } if (Consts.RECEIPT_INVOICE.equals(templateName)){ List invoiceResList = (List) MapUtils.getObject(dataMap, "invoiceResList"); mergeCell(xwpfDocument,0,7,7+invoiceResList.size()-1,Arrays.asList(5,6)); } String docxFile = "/"+ UUID.randomUUID().toString().replace("-", "") + ".docx"; //通过填参 生产文件 并放到 upload 文件夹中 String path = Consts.uploadAbsDir(); String pathFile = path + docxFile; os = new FileOutputStream(pathFile); xwpfDocument.write(os); log.info("文件路径:{}", kkfileConfig.getLocalfileUrl() + File.separator + Consts.UPLOAD_PATH_NAME + docxFile); String previewUrl = WebUtils.getBaseUrl() + "onlinePreview?url=" + URLEncoder.encode(Base64.encodeBase64String((kkfileConfig.getLocalfileUrl() + File.separator + Consts.UPLOAD_PATH_NAME + docxFile).getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8.name()); return previewUrl; } finally { if (os != null) { os.close(); } } } /** * 合并单元格 * @param xwpfDocument 文档对象 * @param tableIndex 表格索引 * @param beginRowIndex 开始行索引 * @param endRowIndex 结束行索引 * @param collIndexs 合并列索引 */ public static void mergeCell(XWPFDocument xwpfDocument,int tableIndex,int beginRowIndex,int endRowIndex,List collIndexs){ //获取文档中指定的表格对象 XWPFTable table = xwpfDocument.getTableArray(tableIndex); if (beginRowIndex == endRowIndex || beginRowIndex > endRowIndex){ return; } //合并单元格的第一个单元格 CTVMerge startMerge = CTVMerge.Factory.newInstance(); startMerge.setVal(STMerge.RESTART); //合并行单元格之后的单元格 CTVMerge endMerge = CTVMerge.Factory.newInstance(); endMerge.setVal(STMerge.CONTINUE); for (Integer collIndex : collIndexs){ table.getRow(beginRowIndex).getCell(collIndex).getCTTc().getTcPr().setVMerge(startMerge); for (int i = beginRowIndex + 1; i <= endRowIndex; i++){ table.getRow(i).getCell(collIndex).getCTTc().getTcPr().setVMerge(endMerge); } } } /** * 获取扩展名 * * @param fileName 文件名 * @return 扩展名 */ public static final String getExtension(String fileName) { return FilenameUtils.getExtension(fileName); } /** * 获取绝对路径 * * @param uploadDir 上传得目录 * @param fileName 文件 * @return 获取文件绝对路径 * @throws IOException 异常 */ public File getAbsoluteFile(String uploadDir, String fileName) throws IOException { File desc = new File(uploadDir + File.separator + fileName); if (!desc.exists()) { if (!desc.getParentFile().exists()) { desc.getParentFile().mkdirs(); } } return desc; } public File download(String templateName, Map dataMap) throws Exception { OutputStream os = null; try { File outFile = new File(Consts.uploadAbsDir()); if (!outFile.exists()) { outFile.mkdirs(); } String templatePath = Consts.getTemplatePath(); String abTemplatePath = templatePath + File.separator + templateName + ".docx"; //判断模板是否存在 File file = new File(abTemplatePath); if(!file.exists()){ throw new KkFileException("请先上传模板!"); } XWPFDocument xwpfDocument = WordExportUtil.exportWord07(abTemplatePath, dataMap); //如果是签收单,则需要合并单元格 if (Consts.RECEIPT_FORM.equals(templateName)){ List invoiceResList = (List) MapUtils.getObject(dataMap, "invoiceResList"); mergeCell(xwpfDocument,0,6,6+invoiceResList.size()-1,Arrays.asList(4,5)); } String docxFile = "/"+ UUID.randomUUID().toString().replace("-", "") + ".docx"; String pdfName = "/"+ UUID.randomUUID().toString().replace("-", "") + ".pdf"; //通过填参 生产文件 并放到 upload 文件夹中 String path = Consts.uploadAbsDir(); String pathFile = path + docxFile; os = new FileOutputStream(pathFile); xwpfDocument.write(os); String outFilePath = Consts.uploadAbsDir()+ File.separator+ pdfName; officeToPdfService.openOfficeToPDF(pathFile, outFilePath); //返回pdf return new File(outFilePath); //返回word //return new File(pathFile); } finally { if (os != null) { os.close(); } } } }