From 4f4650d3c1343611b05cb17024c5e4d9e7b146df Mon Sep 17 00:00:00 2001 From: Mzaxd Date: Thu, 25 May 2023 15:01:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=96=87=E4=BB=B6=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8E=8B=E7=BC=A9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ReportManageConroller.java | 201 +++++++++++------- .../com/adc/da/report/dao/mysql/FileDao.java | 3 + .../adc/da/report/service/IFileService.java | 14 ++ .../report/service/impl/IFileServiceImpl.java | 26 +++ .../com/adc/da/report/util/LocalFileUtil.java | 36 ++++ .../com/adc/da/report/util/WaterMarkUtil.java | 86 ++++---- .../mybatis/mapper/mysql/FileDao.xml | 20 ++ 7 files changed, 276 insertions(+), 110 deletions(-) create mode 100644 adc-da-report/src/main/resources/mybatis/mapper/mysql/FileDao.xml diff --git a/adc-da-report/src/main/java/com/adc/da/report/controller/ReportManageConroller.java b/adc-da-report/src/main/java/com/adc/da/report/controller/ReportManageConroller.java index a13e4da..fe6358e 100644 --- a/adc-da-report/src/main/java/com/adc/da/report/controller/ReportManageConroller.java +++ b/adc-da-report/src/main/java/com/adc/da/report/controller/ReportManageConroller.java @@ -19,6 +19,7 @@ import com.adc.da.report.vo.ReportVo; import com.adc.da.util.exception.AdcDaBaseException; import com.adc.da.util.http.ResponseMessage; import com.adc.da.util.http.Result; +import com.adc.da.util.utils.CollectionUtils; import com.adc.da.util.utils.FileUtil; import com.adc.da.util.utils.StringUtils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -39,11 +40,11 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import java.io.*; -import java.net.URISyntaxException; import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; +import java.util.zip.ZipOutputStream; import static com.adc.da.report.constant.ReportConstants.FILE_EXTENSIONS_PDF; @@ -407,105 +408,157 @@ public class ReportManageConroller { } - -// /** -// * 下载文件 -// * @param response -// * @param fileId -// * @throws IOException -// */ -// @ApiOperation(value = "|下载文件") -// @GetMapping("/download") -// public void downloadPic(HttpServletResponse response, String fileId) throws IOException { -// -// try { -// FileEntity entity = fileService.getFile(fileId); -// String fileName = entity.getFileName(); -// String filename = URLEncoder.encode(fileName, "UTF-8"); -// response.setHeader("Content-Disposition", "attachment;filename=" + filename); -// response.setContentType("application/x-download"); -//// File file = new File(uploadFile, entity.getSavePath()); -// // 上传方式:阿里云:alioss 华为云:hwobs -// if ("hwobs".equals(uploadType)){ -// obsBootUtil.downFile(entity.getSavePath(), response); -// }else if ("alioss".equals(uploadType)){ -// ossClientUtil.downloadFileFromOSS(entity.getSavePath(),response); -// }else if ("local".equals(uploadType)){ -// localFileUtil.downFile(entity.getSavePath(), response); -// } -// if (FILE_EXTENSIONS_PDF.equalsIgnoreCase(FileUtil.getFileExtension(fileName))) { -// String text = "text"; -// PdfUtil.addWatermarkToPdf(response, text); -// } -// } catch (Exception ex) { -// log.info(ex.getMessage(),ex); -// } -// } - /** * 下载文件 * @param response - * @param fileId + * @param fileIds * @throws IOException */ @ApiOperation(value = "|下载文件") @GetMapping("/download") - public void downloadPic(HttpServletResponse response, String fileId,String repostId) throws IOException { + public void download(HttpServletResponse response, String[] fileIds, String reportId) throws IOException { + List fileIdList = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(fileIds)) { + fileIdList = Arrays.asList(fileIds); + } - if (StrUtil.isNotBlank(repostId)) { + if (StrUtil.isBlank(reportId)) { + throw new AdcDaBaseException("缺少报告id"); + } + + if (StrUtil.isNotBlank(reportId)) { //校验权限 - if (!reportService.isAllowDownload(UserUtils.getUserId(), repostId)) { + if (!reportService.isAllowDownload(UserUtils.getUserId(), reportId)) { throw new AdcDaBaseException("无此报告下载权限"); } } - try { - FileEntity entity = fileService.getFile(fileId); - String fileName = entity.getFileName(); - String filename = URLEncoder.encode(fileName, "UTF-8"); - response.setHeader("Content-Disposition", "attachment;filename=" + filename); - response.setContentType("application/x-download"); + response.setContentType("application/x-download"); + //待下载的文件id List + List downLoadFileIds; - //PDF文件需要加水印 - if (FILE_EXTENSIONS_PDF.equalsIgnoreCase(FileUtil.getFileExtension(fileName))) { - String path = uploadFile + entity.getSavePath(); - FileOutputStream outputStream = new FileOutputStream(path); - // 上传方式:阿里云:alioss 华为云:hwobs - if ("hwobs".equals(uploadType)) { - obsBootUtil.getFileFromOBS(entity.getSavePath(), outputStream); - } else if ("alioss".equals(uploadType)) { - ossClientUtil.getFileFromOSS(entity.getSavePath(), outputStream); - } else if ("local".equals(uploadType)) { - localFileUtil.getFileFromLocal(entity.getSavePath(), outputStream); - } - //给文件添加水印 - waterMarkUtil.addWatermarkToPdf(new FileInputStream(path), response); + //判断是从列表页面下载 还是预览页面下载 + if (CollectionUtils.isEmpty(fileIdList)) { + //列表点击下载按钮 + downLoadFileIds = fileService.getDownloadFileIds(reportId); + if (CollectionUtils.isEmpty(downLoadFileIds)) { + log.info("无法下载,原因可能是无可下载文件(xls/xlsx文件无法下载)"); + String zipFileName = "empty.zip"; // 压缩文件名 + try { + // 设置响应头信息 + response.setContentType("application/zip"); + response.setHeader("Content-Disposition", "attachment; filename=" + zipFileName); - } else { - // 上传方式:阿里云:alioss 华为云:hwobs 本地:local - if ("hwobs".equals(uploadType)){ - obsBootUtil.downFile(entity.getSavePath(), response); - }else if ("alioss".equals(uploadType)){ - ossClientUtil.downloadFileFromOSS(entity.getSavePath(),response); - }else if ("local".equals(uploadType)){ - localFileUtil.downFile(entity.getSavePath(), response); + // 创建空的ZipOutputStream + ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream()); + zipOutputStream.close(); + return; + } catch (IOException e) { + e.printStackTrace(); } } - - } catch (Exception ex) { - log.info(ex.getMessage(),ex); + } else { + //检测是否要下载Excel文件 + if (fileService.hasExcelFileId(fileIdList)) { + throw new AdcDaBaseException("不可以下载Excel文件"); + } + //预览点击下载按钮 + downLoadFileIds = fileIdList; } - if (StringUtils.isNotEmpty(repostId)){ + + //如果只下载一个文件,则不压缩 + if (downLoadFileIds.size() == 1) { + String fileId = downLoadFileIds.get(0); + try { + FileEntity entity = fileService.getFile(fileId); + String fileName = entity.getFileName(); + // 获取编码后的文件名 + String encodedFileName = URLEncoder.encode(fileName, "UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName); + + //PDF文件需要加水印 + if (FILE_EXTENSIONS_PDF.equalsIgnoreCase(FileUtil.getFileExtension(fileName))) { + String path = uploadFile + entity.getSavePath(); + FileOutputStream outputStream = new FileOutputStream(path); + // 上传方式:阿里云:alioss 华为云:hwobs + if ("hwobs".equals(uploadType)) { + obsBootUtil.getFileFromOBS(entity.getSavePath(), outputStream); + } else if ("alioss".equals(uploadType)) { + ossClientUtil.getFileFromOSS(entity.getSavePath(), outputStream); + } else if ("local".equals(uploadType)) { + localFileUtil.getFileFromLocal(entity.getSavePath(), outputStream); + } + //给文件添加水印 + waterMarkUtil.addWatermarkToPdfAndWriteToResponse(new FileInputStream(path), response); + outputStream.close(); + } else { + // 上传方式:阿里云:alioss 华为云:hwobs 本地:local + if ("hwobs".equals(uploadType)){ + obsBootUtil.downFile(entity.getSavePath(), response); + }else if ("alioss".equals(uploadType)){ + ossClientUtil.downloadFileFromOSS(entity.getSavePath(),response); + }else if ("local".equals(uploadType)){ + localFileUtil.downFile(entity.getSavePath(), response); + } + } + + } catch (Exception ex) { + log.info(ex.getMessage(),ex); + } + } else { + List filePaths = new ArrayList<>(); + String reportName = reportDao.selectById(reportId).getName() + ".zip"; + // 获取编码后的文件名 + String encodedFileName = URLEncoder.encode(reportName, "UTF-8"); + // 设置响应头信息 + response.setContentType("application/zip"); + response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName); + response.setHeader("Content-Transfer-Encoding", "binary"); + response.setHeader("Pragma", "public"); + response.setHeader("Cache-Control", "no-store"); + response.setHeader("Expires", "0"); + + for (String fileId : downLoadFileIds) { + try { + FileEntity entity = fileService.getFile(fileId); + String fileName = entity.getFileName(); + + String path = uploadFile + entity.getSavePath(); + FileOutputStream outputStream = new FileOutputStream(path); + filePaths.add(path); + + // 上传方式:阿里云:alioss 华为云:hwobs + if ("hwobs".equals(uploadType)) { + obsBootUtil.getFileFromOBS(entity.getSavePath(), outputStream); + } else if ("alioss".equals(uploadType)) { + ossClientUtil.getFileFromOSS(entity.getSavePath(), outputStream); + } else if ("local".equals(uploadType)) { + localFileUtil.getFileFromLocal(entity.getSavePath(), outputStream); + } + //PDF文件需要加水印 + if (FILE_EXTENSIONS_PDF.equalsIgnoreCase(FileUtil.getFileExtension(fileName))) { + waterMarkUtil.savePdfToFile(new FileInputStream(path), path); + } + outputStream.close(); + } catch (Exception ex) { + log.info(ex.getMessage(),ex); + } + } + //将文件压缩 写入Response + localFileUtil.compressFiles(filePaths, response); + } + + if (StringUtils.isNotEmpty(reportId)){ ReportLogBook reportLog = new ReportLogBook(); reportLog.setId(UUID.randomUUID().toString().replaceAll("-","")); - reportLog.setReportId(repostId); + reportLog.setReportId(reportId); reportLog.setType(2); reportLog.setUpdateTime(new Date()); reportLog.setUserId(UserUtils.getUserId()); reportLogBookDao.insert(reportLog); //报告日志管理 - ReportEntity reportEntity = reportDao.selectById(repostId); + ReportEntity reportEntity = reportDao.selectById(reportId); reportLog(reportEntity.getName(),"下载"); } } diff --git a/adc-da-report/src/main/java/com/adc/da/report/dao/mysql/FileDao.java b/adc-da-report/src/main/java/com/adc/da/report/dao/mysql/FileDao.java index 74087c8..5a71440 100644 --- a/adc-da-report/src/main/java/com/adc/da/report/dao/mysql/FileDao.java +++ b/adc-da-report/src/main/java/com/adc/da/report/dao/mysql/FileDao.java @@ -3,9 +3,12 @@ package com.adc.da.report.dao.mysql; import com.adc.da.report.eo.FileEntity; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import java.util.List; + /** * @Author doudxw * @Date 2021/6/28 15:19 */ public interface FileDao extends BaseMapper { + List getDownloadFileIds(String reportId); } diff --git a/adc-da-report/src/main/java/com/adc/da/report/service/IFileService.java b/adc-da-report/src/main/java/com/adc/da/report/service/IFileService.java index 7c875de..b5e104e 100644 --- a/adc-da-report/src/main/java/com/adc/da/report/service/IFileService.java +++ b/adc-da-report/src/main/java/com/adc/da/report/service/IFileService.java @@ -28,5 +28,19 @@ public interface IFileService { List> batchUploadFile(MultipartFile[] files); + /** + * 判断传进来的fileIds里有没有Excel的fileId + * @param fileIds + * @return + */ + boolean hasExcelFileId(List fileIds); + + /** + * 根据报告id返回所有可以下载的文件id + * @param reportId + * @return + */ + List getDownloadFileIds(String reportId); + Map uploadFile(MultipartFile file); } diff --git a/adc-da-report/src/main/java/com/adc/da/report/service/impl/IFileServiceImpl.java b/adc-da-report/src/main/java/com/adc/da/report/service/impl/IFileServiceImpl.java index 4ad2e85..b78e80d 100644 --- a/adc-da-report/src/main/java/com/adc/da/report/service/impl/IFileServiceImpl.java +++ b/adc-da-report/src/main/java/com/adc/da/report/service/impl/IFileServiceImpl.java @@ -1,11 +1,14 @@ package com.adc.da.report.service.impl; +import com.adc.da.report.constant.ReportConstants; import com.adc.da.report.dao.mysql.FileDao; import com.adc.da.report.eo.FileEntity; import com.adc.da.report.service.IFileService; import com.adc.da.report.util.LocalFileUtil; import com.adc.da.report.util.OSSClientUtil; import com.adc.da.report.util.ObsBootUtil; +import com.adc.da.util.utils.CollectionUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -175,4 +178,27 @@ public class IFileServiceImpl implements IFileService { this.addFile(entity); return resultMap; } + + @Override + public boolean hasExcelFileId(List fileIds) { + boolean hasExcelFileId = false; + LambdaQueryWrapper fileEntityLambdaQueryWrapper = new LambdaQueryWrapper<>(); + fileEntityLambdaQueryWrapper.in(FileEntity::getFileId, fileIds); + List fileEntities = fileDao.selectList(fileEntityLambdaQueryWrapper); + if (CollectionUtils.isNotEmpty(fileEntities)) { + for (FileEntity fileEntity : fileEntities) { + if (ReportConstants.FILE_EXTENSIONS_XLS.equals(fileEntity.getFileType()) || + ReportConstants.FILE_EXTENSIONS_XLSX.equals(fileEntity.getFileType())) { + hasExcelFileId = true; + break; + } + } + } + return hasExcelFileId; + } + + @Override + public List getDownloadFileIds(String reportId) { + return fileDao.getDownloadFileIds(reportId); + } } diff --git a/adc-da-report/src/main/java/com/adc/da/report/util/LocalFileUtil.java b/adc-da-report/src/main/java/com/adc/da/report/util/LocalFileUtil.java index 6204671..b4adc83 100644 --- a/adc-da-report/src/main/java/com/adc/da/report/util/LocalFileUtil.java +++ b/adc-da-report/src/main/java/com/adc/da/report/util/LocalFileUtil.java @@ -10,6 +10,9 @@ import javax.servlet.http.HttpServletResponse; import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * @author Caihaohan @@ -24,6 +27,39 @@ public class LocalFileUtil { @Value("${localFilePath}") private String localFilePath; + public void compressFiles(List filePaths, HttpServletResponse response) { + try { + // 创建输出流 + OutputStream outputStream = response.getOutputStream(); + ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream); + + byte[] buffer = new byte[1024]; + + for (String filePath : filePaths) { + File sourceFile = new File(filePath); + + ZipEntry zipEntry = new ZipEntry(sourceFile.getName()); + zipOutputStream.putNextEntry(zipEntry); + + FileInputStream fileInputStream = new FileInputStream(sourceFile); + + int len; + while ((len = fileInputStream.read(buffer)) > 0) { + zipOutputStream.write(buffer, 0, len); + } + + fileInputStream.close(); + zipOutputStream.closeEntry(); + } + + zipOutputStream.close(); + + System.out.println("文件成功压缩成ZIP格式并写入 HttpServletResponse!"); + } catch (IOException e) { + e.printStackTrace(); + } + } + public void saveFile(InputStream fileInputStream, String fileName) throws IOException { String path = localFilePath + fileName; FileOutputStream fileOutputStream = new FileOutputStream(path); diff --git a/adc-da-report/src/main/java/com/adc/da/report/util/WaterMarkUtil.java b/adc-da-report/src/main/java/com/adc/da/report/util/WaterMarkUtil.java index 48350ae..72606e1 100644 --- a/adc-da-report/src/main/java/com/adc/da/report/util/WaterMarkUtil.java +++ b/adc-da-report/src/main/java/com/adc/da/report/util/WaterMarkUtil.java @@ -21,6 +21,7 @@ import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.awt.*; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.time.LocalDate; @@ -52,49 +53,62 @@ public class WaterMarkUtil { return configValue + " " + userName + " " + formattedDate; } - public void addWatermarkToPdf(FileInputStream inputStream, HttpServletResponse response) throws IOException { + public PDDocument addWatermarkToPdf(FileInputStream inputStream) throws IOException { String watermarkText = getWaterMarkConfig(); - // 加载PDF文档 - try (PDDocument document = PDDocument.load(inputStream)) { - // 设置水印字体、字体大小、颜色和透明度 - ClassPathResource fontResource = new ClassPathResource("fonts/SourceHanSerif-VF.ttf"); - PDType0Font font = PDType0Font.load(document, fontResource.getInputStream()); - float fontSize = 13.0f; - Color color = new Color(100, 100, 100, 0); - PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState(); - graphicsState.setNonStrokingAlphaConstant(0.4f); - graphicsState.setStrokingAlphaConstant(0.4f); + PDDocument document = PDDocument.load(inputStream); - // 遍历每一页并添加水印 - for (PDPage page : document.getPages()) { - // 获取页面尺寸以计算水印位置 - PDRectangle pageSize = page.getMediaBox(); - float xStep = pageSize.getWidth() / 4; - float yStep = pageSize.getHeight() / 4; - float rotationInRadians = (float) Math.toRadians(20); + // 设置水印字体、字体大小、颜色和透明度 + ClassPathResource fontResource = new ClassPathResource("fonts/SourceHanSerif-VF.ttf"); + PDType0Font font = PDType0Font.load(document, fontResource.getInputStream()); + float fontSize = 13.0f; + Color color = new Color(100, 100, 100, 0); + PDExtendedGraphicsState graphicsState = new PDExtendedGraphicsState(); + graphicsState.setNonStrokingAlphaConstant(0.4f); + graphicsState.setStrokingAlphaConstant(0.4f); - for (float xPosition = pageSize.getLowerLeftX(); xPosition <= pageSize.getWidth(); xPosition += xStep) { - for (float yPosition = pageSize.getLowerLeftY(); yPosition <= pageSize.getHeight(); yPosition += yStep) { - // 创建内容流并设置图形状态参数 - try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true)) { - contentStream.setGraphicsStateParameters(graphicsState); - contentStream.setFont(font, fontSize); - contentStream.setNonStrokingColor(color); - contentStream.beginText(); - contentStream.setRenderingMode(RenderingMode.FILL); - // 设置水印文本的旋转和位置 - contentStream.setTextMatrix(Matrix.getRotateInstance(rotationInRadians, xPosition, yPosition)); - contentStream.showText(watermarkText); - contentStream.endText(); - } + // 遍历每一页并添加水印 + for (PDPage page : document.getPages()) { + // 获取页面尺寸以计算水印位置 + PDRectangle pageSize = page.getMediaBox(); + float xStep = pageSize.getWidth() / 4; + float yStep = pageSize.getHeight() / 4; + float rotationInRadians = (float) Math.toRadians(20); + + for (float xPosition = pageSize.getLowerLeftX(); xPosition <= pageSize.getWidth(); xPosition += xStep) { + for (float yPosition = pageSize.getLowerLeftY(); yPosition <= pageSize.getHeight(); yPosition += yStep) { + // 创建内容流并设置图形状态参数 + try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true)) { + contentStream.setGraphicsStateParameters(graphicsState); + contentStream.setFont(font, fontSize); + contentStream.setNonStrokingColor(color); + contentStream.beginText(); + contentStream.setRenderingMode(RenderingMode.FILL); + // 设置水印文本的旋转和位置 + contentStream.setTextMatrix(Matrix.getRotateInstance(rotationInRadians, xPosition, yPosition)); + contentStream.showText(watermarkText); + contentStream.endText(); } } } + } + return document; + } - // 将处理后的PDF写入HTTP响应 - try (OutputStream outputStream = response.getOutputStream()) { - document.save(outputStream); - } + + public void savePdfToFile(FileInputStream inputStream, String outputPath) throws IOException { + PDDocument pdDocument = addWatermarkToPdf(inputStream); + try (FileOutputStream outputStream = new FileOutputStream(outputPath)) { + pdDocument.save(outputStream); } } + + + public void addWatermarkToPdfAndWriteToResponse(FileInputStream inputStream, HttpServletResponse response) throws IOException { + PDDocument document = addWatermarkToPdf(inputStream); + try (OutputStream outputStream = response.getOutputStream()) { + document.save(outputStream); + } + document.close(); + } + } diff --git a/adc-da-report/src/main/resources/mybatis/mapper/mysql/FileDao.xml b/adc-da-report/src/main/resources/mybatis/mapper/mysql/FileDao.xml new file mode 100644 index 0000000..30a81a8 --- /dev/null +++ b/adc-da-report/src/main/resources/mybatis/mapper/mysql/FileDao.xml @@ -0,0 +1,20 @@ + + + + + +