package com.xunmei.file.service; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson2.JSON; import com.lowagie.text.*; import com.lowagie.text.pdf.*; import com.xunmei.common.core.constant.CacheConstants; import com.xunmei.common.core.domain.IdName; import com.xunmei.common.core.domain.registerbook.dto.CoreRegisterBookPdfExportDto; import com.xunmei.common.core.domain.registerbook.dto.ExportPdfDto; import com.xunmei.common.core.domain.registerbook.vo.CoreRegisterBookPdfPageVo; import com.xunmei.common.core.domain.registerbook.vo.PdfLocalFileTempVo; import com.xunmei.common.core.domain.registerbook.vo.PdfToZipTempVo; import com.xunmei.common.core.enums.RegisterBookType; import com.xunmei.common.core.utils.DateHelper; import com.xunmei.common.core.utils.uuid.UUID; import com.xunmei.common.redis.utils.RedisUtils; import com.xunmei.file.utils.FileDownUtils; import com.xunmei.file.utils.FileUploadUtils; import com.xunmei.file.utils.PdfUtil; import com.xunmei.file.vo.FileBase64Vo; import com.xunmei.file.vo.ItextPdfTableVo; import com.xunmei.file.vo.PdfFilePathVo; import com.xunmei.system.api.domain.SafeCheckTaskRegisterBookVo; import com.xunmei.system.api.vo.SysOrgVO; import io.netty.util.internal.StringUtil; import org.apache.commons.lang3.StringUtils; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.core.BoundValueOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.math.BigDecimal; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.time.Duration; import java.util.List; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * 本地文件存储 * * @author xunmei */ @Primary @Service public class LocalSysFileServiceImpl implements ISysFileService { private static final Logger log = LoggerFactory.getLogger(LocalSysFileServiceImpl.class); public static final String TEMP_DIR_NAME = "registerBookPdfBatchExportTempDir"; @Value("${file.path}") private String localFilePath; @Value("${file.prefix}") public String prefix; @Autowired private HttpServletRequest request; @Autowired private StringRedisTemplate redisTemplate; private static PdfFilePathVo getLocalFilePath(String localFilePath, String businessType, String fileName) { fileName = filterPath(fileName); businessType = filterPath(businessType); final String path = File.separator + businessType + File.separator + DateUtil.format(new Date(), "yyyy" + File.separator + "MM" + File.separator + "dd" + File.separator); final File file = new File(localFilePath + path); if (!file.exists()) { file.mkdirs(); } PdfFilePathVo pathVo = new PdfFilePathVo(); pathVo.setAbsolutePath(localFilePath + path + fileName); pathVo.setRelativePath(path + fileName); return pathVo; } /** * 修复路径操纵bug * * @param param * @return */ private static String filterPath(String param) { Pattern pattern = Pattern.compile("[/\\:*?<>|]"); Matcher matcher = pattern.matcher(param); param = matcher.replaceAll(""); return param; } private static String filterHeader(String param) { Pattern pattern = Pattern.compile("[/\\:*?<>|=\\r\\n]"); Matcher matcher = pattern.matcher(param); param = matcher.replaceAll(""); return param; } /** * 本地文件上传接口 * * @param file 上传的文件 * @return 访问地址 * @throws Exception */ @Override public String uploadFile(MultipartFile file) throws Exception { String name = FileUploadUtils.upload(localFilePath, file); // String url = domain + localFilePrefix + name; return name; } @Override public String uploadFile(MultipartFile file, String busType) throws Exception { String name = FileUploadUtils.upload(localFilePath, file, busType); // String url = domain + localFilePrefix + name; return name; } @Override public void downloadFile(HttpServletResponse response, String filePath) { ByteArrayOutputStream out = null; try { filePath = localFilePath + filePath; filePath = URLDecoder.decode(filePath, "UTF-8"); out = FileDownUtils.downloadFile(filePath); String fileSuffix = FileDownUtils.getFileSuffix(filePath); String formFileName = UUID.randomUUID().toString() + fileSuffix; String userAgent = request.getHeader("User-Agent"); // 针对IE或者以IE为内核的浏览器: if (StringUtils.isNotEmpty(userAgent) && (userAgent.contains("MSIE") || userAgent.contains("Trident"))) { formFileName = java.net.URLEncoder.encode(formFileName, "UTF-8"); } else { // 非IE浏览器的处理: formFileName = new String(formFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); } response.reset(); response.setCharacterEncoding("UTF-8"); response.addHeader("Content-Disposition", "attachment;filename=" + formFileName); response.addHeader("Content-Length", "" + out.size()); out.writeTo(response.getOutputStream()); response.setContentType("application/octet-stream"); out.flush(); out.close(); } catch (IOException e) { log.error("文件下载失败! "); e.printStackTrace(); } finally { try { if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } } @Override public void getFileStream(String path, HttpServletResponse response) { if (ObjectUtil.isEmpty(path)) { return; } if (!path.startsWith(this.localFilePath)) { path = this.localFilePath + path; } try { File file = new File(path); FileInputStream inputStream = new FileInputStream(file); int i = path.lastIndexOf(File.separator); String fileName = path.substring(i + 1); fileName = filterHeader(fileName); // 设置响应头 response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName); response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); // 将文件流写入响应输出流 byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { response.getOutputStream().write(buffer, 0, bytesRead); } inputStream.close(); } catch (Exception e) { log.error("获取文件内容失败!"); } } @Override public String getRelativePath(String path) { if (ObjectUtil.isEmpty(path)) { return null; } if (path.startsWith(this.localFilePath)) { return path.substring(this.localFilePath.length()); } return path; } private void dealHeader(Map dataMap, Document document, BaseFont abf, BaseFont fs) throws DocumentException { //处理标题 Paragraph p = new Paragraph("日常履职登记簿", new Font(fs, 20, Font.NORMAL)); p.setAlignment(Paragraph.ALIGN_CENTER); //段落下空白 p.setSpacingAfter(10f); document.add(p); //日期 String blankStr1 = " "; String blankStr2 = " "; Paragraph p2 = new Paragraph(dataMap.get("orgName") + blankStr1 + dataMap.get("dateStr") + blankStr2, new Font(abf, 10, Font.NORMAL)); p2.setAlignment(Paragraph.ALIGN_RIGHT); document.add(p2); } @Override public String generateEduTrainingPdf(Map data) throws Exception { PdfFilePathVo pathVo = getLocalFilePath(localFilePath, "edu", data.get("fileName").toString()); log.info("开始生成教育培训登记簿,当前绝对地址为:{}", pathVo.getAbsolutePath()); final ItextPdfTableVo pdfTableVo = PdfUtil.createTable(pathVo.getAbsolutePath(), 6, 10); final Document document = pdfTableVo.getDocument(); final PdfWriter writer = pdfTableVo.getWriter(); final PdfPTable table = pdfTableVo.getTable(); final BaseFont fs = pdfTableVo.getFs(); final Font tableFont = pdfTableVo.getTableFont(); PdfUtil.dealHeader(document, fs, "学 习 教 育 记 录", 24); PdfUtil.dealEduBody(document, table, tableFont, data); document.close(); writer.close(); log.info("教育培训登记簿生成结束,当前绝对地址为:{}", pathVo.getAbsolutePath()); //此处返回 /statics/edu/xxx.pdf return this.prefix + pathVo.getRelativePath(); } @Override public String generateResumptionPdf(Map data) throws Exception { PdfFilePathVo pathVo = getLocalFilePath(localFilePath, "resumption", data.get("fileName").toString()); log.info("开始生成履职登记簿,当前绝对地址为:{}", pathVo.getAbsolutePath()); Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pathVo.getAbsolutePath())); document.open(); // 使用语言包字体 BaseFont abf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); // 外部字体 BaseFont fs = BaseFont.createFont("/fonts/msyh.ttc,1", BaseFont.IDENTITY_H, BaseFont.EMBEDDED); Font tableFont = new Font(fs, 12, Font.NORMAL); PdfPTable table = new PdfPTable(8); table.setSpacingBefore(16f); this.dealHeader(data, document, abf, fs); PdfUtil.dealResumptionBody(document, table, tableFont, data); document.close(); writer.close(); log.info("履职登记簿生成结束,当前绝对地址为:{}", pathVo.getAbsolutePath()); //此处返回 /statics/edu/xxx.pdf return this.prefix + pathVo.getRelativePath(); } @Override public String generateSafeCheckPdf(SafeCheckTaskRegisterBookVo data) throws Exception { PdfFilePathVo pathVo = getLocalFilePath(localFilePath, "safeCheck", data.getDest()); log.info("开始生成安全检查登记簿,当前绝对地址为:{}", pathVo.getAbsolutePath()); final ItextPdfTableVo pdfTableVo = PdfUtil.createTable(pathVo.getAbsolutePath(), 46, 7); final Document document = pdfTableVo.getDocument(); final PdfWriter writer = pdfTableVo.getWriter(); final PdfPTable table = pdfTableVo.getTable(); final BaseFont fs = pdfTableVo.getFs(); final Font tableFont = pdfTableVo.getTableFont(); Font font = new Font(fs, 9, Font.NORMAL); PdfUtil.dealHeader(document, fs, data.getTaskTitle(), 14); //日期 String orgName = "被查支行: " + data.getOrgName(); String checkUser = "检查人签名: " + data.getCheckUserInfo(); String dateStr = data.getDateStr(); PdfPCell orgCell = new PdfPCell(new Phrase(orgName, font)); orgCell.setColspan(16); orgCell.setRowspan(1); orgCell.setBorder(Rectangle.NO_BORDER); orgCell.setHorizontalAlignment(Element.ALIGN_LEFT); PdfPCell checkUserCell = new PdfPCell(new Phrase(checkUser, font)); checkUserCell.setColspan(20); checkUserCell.setRowspan(1); checkUserCell.setBorder(Rectangle.NO_BORDER); checkUserCell.setHorizontalAlignment(Element.ALIGN_CENTER); PdfPCell dateStrCell = new PdfPCell(new Phrase(dateStr, font)); dateStrCell.setColspan(10); dateStrCell.setRowspan(1); dateStrCell.setBorder(Rectangle.NO_BORDER); dateStrCell.setHorizontalAlignment(Element.ALIGN_RIGHT); table.addCell(orgCell); table.addCell(checkUserCell); table.addCell(dateStrCell); Font titleFont = new Font(fs, 8, Font.NORMAL); PdfUtil.dealSafeCheckPBody(document, table, tableFont, titleFont, data.getCheckDatas()); document.close(); writer.close(); log.info("安全检查登记簿生成结束,当前绝对地址为:{}", pathVo.getAbsolutePath()); //此处返回 /statics/edu/xxx.pdf return this.prefix + pathVo.getRelativePath(); } @Override public String generateDrillPdf(Map data) throws Exception { PdfFilePathVo pathVo = getLocalFilePath(localFilePath, "drill", data.get("fileName").toString()); log.info("开始生成预案演练登记簿,当前绝对地址为:{}", pathVo.getAbsolutePath()); final ItextPdfTableVo pdfTableVo = PdfUtil.createTable(pathVo.getAbsolutePath(), 6, 10); final Document document = pdfTableVo.getDocument(); final PdfWriter writer = pdfTableVo.getWriter(); final PdfPTable table = pdfTableVo.getTable(); final BaseFont fs = pdfTableVo.getFs(); final Font tableFont = pdfTableVo.getTableFont(); PdfUtil.dealHeader(document, fs, "预 案 演 练 记 录", 24); PdfUtil.dealDrillBody(document, table, tableFont, data); document.close(); writer.close(); log.info("预案演练登记簿生成结束,当前绝对地址为:{}", pathVo.getAbsolutePath()); //此处返回 /statics/edu/xxx.pdf return this.prefix + pathVo.getRelativePath(); } @Override public String uploadFileBase64(FileBase64Vo file) throws Exception { String filePath = FileUploadUtils.uploadBase64(localFilePath, file); return filePath; } /** * 履职转pdf * * @param dest * @throws IOException * @throws DocumentException */ public void dataToPdf(String dest) throws IOException, DocumentException { Document document = new Document(); PdfWriter.getInstance(document, new FileOutputStream(dest)); document.open(); // 使用语言包字体 BaseFont abf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED); //字体 Font font = new Font(abf, 8); //段落 Paragraph p = new Paragraph("测试结算单", new Font(abf, 12, Font.BOLD)); p.setAlignment(Paragraph.ALIGN_CENTER); document.add(p); //表格 PdfPTable table = new PdfPTable(8);//numcolumns:列数 table.setSpacingBefore(16f);//表格与上面段落的空隙 //表格列创建并赋值 PdfPCell cell = new PdfPCell(new Phrase("单位名称:测试有限公司", font)); cell.setHorizontalAlignment(Element.ALIGN_LEFT);//居中 cell.disableBorderSide(13);//去除左右上边框,保留下边框 cell.setColspan(4);//合并列数 table.addCell(cell); cell = new PdfPCell(new Phrase("日期:2020-06-05", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.disableBorderSide(13); cell.setColspan(3); table.addCell(cell); cell = new PdfPCell(new Phrase("单位(元)", font)); cell.setHorizontalAlignment(Element.ALIGN_LEFT); cell.disableBorderSide(13); cell.setColspan(1); table.addCell(cell); //首行 cell = new PdfPCell(new Phrase("期间", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setColspan(2); table.addCell(cell); cell = new PdfPCell(new Phrase("月份", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("分类", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("年利率", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("日利率", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("基数", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("利息", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("起始日:2020-03-26\n" + "结束日:2020-04-25", font)); cell.setPadding(16f); cell.setVerticalAlignment(Element.ALIGN_CENTER); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setRowspan(3); cell.setColspan(2); table.addCell(cell); cell = new PdfPCell(new Phrase("4", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("资金", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("1.10%", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("0.000031", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("10598164.91", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("325.01", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("4", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("资金", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("1.10%", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("0.000031", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("-", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("-", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("4", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("资金", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("1.10%", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("0.000031", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("-", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("-", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("合计", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setColspan(7); table.addCell(cell); cell = new PdfPCell(new Phrase("325.01", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("会计制单:", font)); cell.setHorizontalAlignment(Element.ALIGN_LEFT); cell.disableBorderSide(14); cell.setColspan(4); table.addCell(cell); cell = new PdfPCell(new Phrase("复核:", font)); cell.setHorizontalAlignment(Element.ALIGN_LEFT); cell.disableBorderSide(14); cell.setColspan(4); table.addCell(cell); table.setSpacingBefore(16f); document.add(table); //下一页 document.newPage(); //段落 Paragraph p1 = new Paragraph("下一页测试结算单", new Font(abf, 12, Font.BOLD)); p1.setAlignment(Paragraph.ALIGN_CENTER); document.add(p1); //表格 table = new PdfPTable(8);//numcolumns:列数 table.setSpacingBefore(16f);//表格与上面段落的空隙 //表格列创建并赋值 cell = new PdfPCell(new Phrase("单位名称:测试有限公司", font)); cell.setHorizontalAlignment(Element.ALIGN_LEFT);//居中 cell.disableBorderSide(13);//去除左右上边框,保留下边框 cell.setColspan(4);//合并列数 table.addCell(cell); cell = new PdfPCell(new Phrase("日期:2020-06-05", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.disableBorderSide(13); cell.setColspan(3); table.addCell(cell); cell = new PdfPCell(new Phrase("单位(元)", font)); cell.setHorizontalAlignment(Element.ALIGN_LEFT); cell.disableBorderSide(13); cell.setColspan(1); table.addCell(cell); //首行 cell = new PdfPCell(new Phrase("期间", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setColspan(2); table.addCell(cell); cell = new PdfPCell(new Phrase("月份", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("分类", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("年利率", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("日利率", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("基数", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("利息", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("起始日:2020-04-26\n" + "结束日:2020-05-25", font)); cell.setPadding(16f); cell.setVerticalAlignment(Element.ALIGN_CENTER); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setColspan(2); table.addCell(cell); cell = new PdfPCell(new Phrase("4", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("资金", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("1.10%", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("0.000031", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("10598164.91", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("325.01", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("合计", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); cell.setColspan(7); table.addCell(cell); cell = new PdfPCell(new Phrase("325.01", font)); cell.setHorizontalAlignment(Element.ALIGN_CENTER); table.addCell(cell); cell = new PdfPCell(new Phrase("会计制单:", font)); cell.setHorizontalAlignment(Element.ALIGN_LEFT); cell.disableBorderSide(14); cell.setColspan(4); table.addCell(cell); cell = new PdfPCell(new Phrase("复核:", font)); cell.setHorizontalAlignment(Element.ALIGN_LEFT); cell.disableBorderSide(14); cell.setColspan(4); table.addCell(cell); table.setSpacingBefore(16f); document.add(table); document.close(); } @Override public String absolutePath(String path) { if (ObjectUtil.isEmpty(path)) { return StringUtil.EMPTY_STRING; } if (path.startsWith(this.localFilePath)) { return path; } if (path.startsWith(this.prefix)) { return this.localFilePath + path.substring(this.prefix.length()); } return StringUtil.EMPTY_STRING; } @Override public InputStream getFileStream(String path) throws IOException { String absolutePath = this.absolutePath(path); File file = new File(absolutePath); return Files.newInputStream(file.toPath()); } @Override public void registerBookCompressPdf(ExportPdfDto pdfDto, HttpServletResponse response) throws IOException { final List registerBookPdfList = pdfDto.getRegisterBookPdfList(); try { String zipName = URLEncoder.encode(pdfDto.getFileName() + DateHelper.getDateString(new Date()) + ".zip", "UTF-8"); final CountDownLatch count = new CountDownLatch(registerBookPdfList.size()); ServletOutputStream outputStream = response.getOutputStream(); ZipOutputStream zos = new ZipOutputStream(outputStream); response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment; filename=" + zipName); List pdfToZipTempVoList = registerBookPdfList.parallelStream().map(pdf -> { return resolve(pdf, count); }).filter(Objects::nonNull) .collect(Collectors.toList()); count.await(); pdfToZipTempVoList.removeIf(pdfToZipTempVo -> !FileUtil.exist(pdfToZipTempVo.getFile())); log.info("登记簿全部下载完成,开始压缩文件,数量:{}", pdfToZipTempVoList.size()); for (PdfToZipTempVo tempVo : pdfToZipTempVoList) { log.info("当前开始处理第{}个文件 ", pdfToZipTempVoList.indexOf(tempVo) + 1); deal(zos, tempVo); } zos.flush(); zos.close(); outputStream.close(); log.info("登记簿批量导出压缩文件完成,文件数量:{}", pdfToZipTempVoList.size()); } catch (Throwable e) { String errMsg = String.format("登记簿导出失败:%s", e); log.error(errMsg); } finally { final File temp = new File(TEMP_DIR_NAME); if (temp.exists()) { FileUtil.del(temp); log.info("临时目录已删除"); } } } public PdfToZipTempVo resolve(CoreRegisterBookPdfPageVo pdf, CountDownLatch count) { final File temp = new File(TEMP_DIR_NAME); if (!temp.exists()) { temp.mkdirs(); } InputStream inputStream; try { inputStream = getFileStream(pdf.getFileUrl()); if (ObjectUtil.isEmpty(inputStream)) { log.error("登记簿导出失败,文件不存在,文件名:{}", pdf.getFileUrl()); return null; } final String pdfFileName = pdf.getFileName(); //pdfFileName==null的时候在下面会报错此处加个判断,要处理问题还需要在问题源头除处理 // registerBookPdfBatchExportTempDir (Is a directory) if (StringUtils.isEmpty(pdfFileName)) { return null; } final PdfToZipTempVo tempVo = new PdfToZipTempVo(); //tempVo.setBytes(bytes); tempVo.setFileName(pdfFileName); tempVo.setOrgId(pdf.getOrgId()); tempVo.setOrgName(pdf.getOrgName()); tempVo.setRegisterBookType(RegisterBookType.getEnums(pdf.getRegisterBookType())); final File file = new File(temp + File.separator + pdfFileName); final FileOutputStream outputStream = new FileOutputStream(file); byte[] buffer = new byte[4096]; int len; while ((len = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, len); } outputStream.close(); inputStream.close(); tempVo.setFile(file); final List> idNameList = pdf.getOrgList(); if (ObjectUtil.isEmpty(idNameList)) { tempVo.setEntryName(File.separator + RegisterBookType.getEnums(pdf.getRegisterBookType()).getText() + File.separator + pdfFileName); return tempVo; } StringJoiner stringJoiner = new StringJoiner(File.separator); for (IdName org : idNameList) { stringJoiner.add(org.getName()); if (idNameList.lastIndexOf(org) == idNameList.size() - 1) { stringJoiner.add(RegisterBookType.getEnums(pdf.getRegisterBookType()).getText() + File.separator + pdfFileName); } } tempVo.setEntryName(stringJoiner.toString()); return tempVo; } catch (Exception e) { log.error("读取文件失败", e); return null; } finally { count.countDown(); } } public void deal(ZipOutputStream zos, PdfToZipTempVo zipTempVo) throws Throwable { zos.setEncoding("GBK"); FileInputStream fileInputStream = new FileInputStream(zipTempVo.getFile()); zos.putNextEntry(new ZipEntry(zipTempVo.getEntryName())); byte[] buffer = new byte[4096]; int len; while ((len = fileInputStream.read(buffer)) > 0) { zos.write(buffer, 0, len); } zos.closeEntry(); fileInputStream.close(); } private List getChildrenList(Long orgId) { List cacheList = RedisUtils.getCacheList(CacheConstants.ORG_CACHE_LIST_KEY); return cacheList.stream() .filter(org -> ObjectUtil.equal(org.getParentId(), orgId)) .collect(Collectors.toList()); } private SysOrgVO getCurOrg(Long orgId) { List cacheList = RedisUtils.getCacheList(CacheConstants.ORG_CACHE_LIST_KEY); return cacheList.stream() .filter(org -> ObjectUtil.equal(org.getId(), orgId)) .findFirst().get(); } @Override public void cutFileCompress(CoreRegisterBookPdfExportDto pdfDto) { SysOrgVO org = getCurOrg(pdfDto.getOrgId()); Date date = new Date(); String fileName = pdfDto.getIsRegisterBookPage() ? org.getName() + "_登记簿_" : org.getName() + "_数据报表_"; //判断需要分几片导出 List> lists = checkSubList(pdfDto); int num = 1; try { for (List list : lists) { CountDownLatch count = new CountDownLatch(list.size()); String zipName = null; String str = lists.size() == 1 ? "" : "_part_" + num; String fileNameStr = fileName + DateHelper.getDateString(new Date()) + str; zipName = URLEncoder.encode(fileNameStr + ".zip", "UTF-8"); List pdfToZipTempVoList = list.parallelStream().map(pdf -> { return resolve(pdf, count); }).filter(Objects::nonNull) .collect(Collectors.toList()); pdfToZipTempVoList.removeIf(pdfToZipTempVo -> !FileUtil.exist(pdfToZipTempVo.getFile())); log.info("登记簿全部下载完成,开始压缩文件,数量:{}", pdfToZipTempVoList.size()); String encodedFileName = URLEncoder.encode(CacheConstants.REGISTER_PDF_FILE_KEY + DateHelper.getDateString(date) + str + ".zip", "UTF-8"); String filePath = this.localFilePath + File.separator + encodedFileName; FileOutputStream fos = new FileOutputStream(filePath); ZipOutputStream zos = new ZipOutputStream(fos); long fileSize = 0L; for (PdfToZipTempVo tempVo : pdfToZipTempVoList) { fileSize += tempVo.getFile().length(); log.info("当前开始处理第{}个文件 ", pdfToZipTempVoList.indexOf(tempVo) + 1); deal(zos, tempVo); } zos.flush(); fos.flush(); zos.close(); fos.close(); num++; saveFileDataToRedis(org, date, zipName, filePath, fileSize, pdfDto); } } catch (Throwable e) { throw new RuntimeException(e); } finally { File file = new File(TEMP_DIR_NAME); if (file.exists()) { FileUtil.del(file); } } } private void saveFileDataToRedis(SysOrgVO org, Date date, String zipName, String localFileName, long fileSize, CoreRegisterBookPdfExportDto pdfDto) throws UnsupportedEncodingException { PdfLocalFileTempVo pdfLocalFileTempVo = new PdfLocalFileTempVo(); pdfLocalFileTempVo.setOrgId(pdfDto.getOrgId()); pdfLocalFileTempVo.setOrgName(org.getName()); pdfLocalFileTempVo.setOrgPath(org.getPath()); pdfLocalFileTempVo.setLocalFileName(localFileName); pdfLocalFileTempVo.setZipName(URLDecoder.decode(zipName, "UTF-8")); pdfLocalFileTempVo.setFileSize(changeUnit(fileSize)); pdfLocalFileTempVo.setDownLoadTime(DateUtil.format(date, "yyyy-MM-dd HH:mm:ss")); pdfLocalFileTempVo.setIsRegisterBookPage(pdfDto.getIsRegisterBookPage()); pdfLocalFileTempVo.setCreateTime(new Date()); //此处localFileName 为文件的绝对路径,存在redis延迟队列中,一个小时后删除文件 localFileName = localFileName.replace(this.localFilePath + File.separator, ""); //此处localFileName 为文件名称,存入redis中,用于页面展示文件名称,下载 //RedisUtils.setCacheObject(URLDecoder.decode(localFileName, "UTF-8"), JSON.toJSONString(pdfLocalFileTempVo),true); final BoundValueOperations ops = redisTemplate.boundValueOps(URLDecoder.decode(localFileName, "UTF-8")); ops.set(JSON.toJSONString(pdfLocalFileTempVo)); ops.expireAt(DateUtil.endOfDay(new Date())); } private List> checkSubList(CoreRegisterBookPdfExportDto pdfDto) { List> list = new ArrayList<>(); List registerBookPdfList = pdfDto.getDataList(); if (CollectionUtil.isEmpty(registerBookPdfList)) { throw new RuntimeException("暂无可下载数据!"); } //小于3000条直接导出 if (registerBookPdfList.size() < 3000) { list.add(registerBookPdfList); return list; } List orgList = getChildrenList(pdfDto.getOrgId()); //大于3000条需要分片 return splitList(registerBookPdfList, orgList, 3000); } public static List> splitList(List list, List orgList, int size) { // 根据机构分组 List> collect = orgList.stream().map(org -> { List arrayList = new ArrayList<>(); for (CoreRegisterBookPdfPageVo pdf : list) { if (pdf.getOrgPath().startsWith(org.getPath())) { arrayList.add(pdf); } } return arrayList; }).filter(ObjectUtil::isNotEmpty).collect(Collectors.toList()); // 合并不满3000的数组 List> merged = new LinkedList<>(); List current = new ArrayList<>(); for (List group : collect) { if (group.size() >= size) { merged.add(group); } else { current.addAll(group); if (current.size() >= size) { merged.add(current); current = new ArrayList<>(); } } } if (!current.isEmpty()) { merged.add(current); } return merged; //return merged.stream().limit(size).collect(Collectors.toList()); } public String changeUnit(Long fileSize) { if (fileSize == null || fileSize == 0) { return "0"; } //将fileSize转换为MB,保留整数 BigDecimal fileSizeMB = new BigDecimal(fileSize).divide(new BigDecimal(1024 * 1024), 2, BigDecimal.ROUND_HALF_UP); return fileSizeMB.toString(); } @Override public void deletedZipFile() { final File dir = new File(this.localFilePath); final File[] files = dir.listFiles(); if (ObjectUtil.isNull(files) || ObjectUtil.isEmpty(files)) { return; } Arrays.stream(files) .filter(file -> file.getName().startsWith(CacheConstants.REGISTER_PDF_FILE_KEY)) .filter(file->file.getName().endsWith(".zip")) .forEach(FileUtil::del); } }