OfficeFilePreviewImpl.java 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. package cn.keking.service.impl;
  2. import cn.keking.config.ConfigConstants;
  3. import cn.keking.model.FileAttribute;
  4. import cn.keking.model.ReturnResponse;
  5. import cn.keking.service.FileHandlerService;
  6. import cn.keking.service.FilePreview;
  7. import cn.keking.service.OfficeToPdfService;
  8. import cn.keking.utils.DownloadUtils;
  9. import cn.keking.utils.KkFileUtils;
  10. import cn.keking.utils.OfficeUtils;
  11. import cn.keking.web.filter.BaseUrlFilter;
  12. import org.apache.commons.lang3.exception.ExceptionUtils;
  13. import org.apache.poi.EncryptedDocumentException;
  14. import org.jodconverter.core.office.OfficeException;
  15. import org.springframework.stereotype.Service;
  16. import org.springframework.ui.Model;
  17. import org.springframework.util.StringUtils;
  18. import java.io.IOException;
  19. import java.net.URLEncoder;
  20. import java.util.List;
  21. /**
  22. * Created by kl on 2018/1/17.
  23. * Content :处理office文件
  24. */
  25. @Service
  26. public class OfficeFilePreviewImpl implements FilePreview {
  27. public static final String OFFICE_PREVIEW_TYPE_IMAGE = "image";
  28. public static final String OFFICE_PREVIEW_TYPE_ALL_IMAGES = "allImages";
  29. private static final String FILE_DIR = ConfigConstants.getFileDir();
  30. private static final String OFFICE_PASSWORD_MSG = "password";
  31. private final FileHandlerService fileHandlerService;
  32. private final OfficeToPdfService officeToPdfService;
  33. private final OtherFilePreviewImpl otherFilePreview;
  34. public OfficeFilePreviewImpl(FileHandlerService fileHandlerService, OfficeToPdfService officeToPdfService, OtherFilePreviewImpl otherFilePreview) {
  35. this.fileHandlerService = fileHandlerService;
  36. this.officeToPdfService = officeToPdfService;
  37. this.otherFilePreview = otherFilePreview;
  38. }
  39. @Override
  40. public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
  41. // 预览Type,参数传了就取参数的,没传取系统默认
  42. String officePreviewType = fileAttribute.getOfficePreviewType();
  43. String baseUrl = BaseUrlFilter.getBaseUrl();
  44. String suffix = fileAttribute.getSuffix();
  45. String fileName = fileAttribute.getName();
  46. String filePassword = fileAttribute.getFilePassword();
  47. boolean forceUpdatedCache=fileAttribute.forceUpdatedCache();
  48. String userToken = fileAttribute.getUserToken();
  49. boolean isHtml = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx") || suffix.equalsIgnoreCase("csv") || suffix.equalsIgnoreCase("xlsm") || suffix.equalsIgnoreCase("xlt") || suffix.equalsIgnoreCase("xltm") || suffix.equalsIgnoreCase("et") || suffix.equalsIgnoreCase("ett") || suffix.equalsIgnoreCase("xlam");
  50. String pdfName = fileName.substring(0, fileName.lastIndexOf(".") ) + suffix +"." +(isHtml ? "html" : "pdf"); //生成文件添加类型后缀 防止同名文件
  51. String cacheFileName = userToken == null ? pdfName : userToken + "_" + pdfName;
  52. String outFilePath = FILE_DIR + cacheFileName;
  53. if (forceUpdatedCache|| !fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
  54. // 下载远程文件到本地,如果文件在本地已存在不会重复下载
  55. ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
  56. if (response.isFailure()) {
  57. return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
  58. }
  59. String filePath = response.getContent();
  60. /*
  61. * 1. 缓存判断-如果文件已经进行转换过,就直接返回,否则执行转换
  62. * 2. 缓存判断-加密文件基于userToken进行缓存,如果没有就不缓存
  63. */
  64. boolean isCached = false;
  65. boolean isUseCached = false;
  66. boolean isPwdProtectedOffice = false;
  67. if (ConfigConstants.isCacheEnabled()) {
  68. // 全局开启缓存
  69. isUseCached = true;
  70. if (!forceUpdatedCache && fileHandlerService.listConvertedFiles().containsKey(cacheFileName)) {
  71. // 存在缓存
  72. isCached = true;
  73. }
  74. if (OfficeUtils.isPwdProtected(filePath)) {
  75. isPwdProtectedOffice = true;
  76. if (!StringUtils.hasLength(userToken)) {
  77. // 不缓存没有userToken的加密文件
  78. isUseCached = false;
  79. }
  80. }
  81. } else {
  82. isPwdProtectedOffice = OfficeUtils.isPwdProtected(filePath);
  83. }
  84. if (!isCached) {
  85. // 没有缓存执行转换逻辑
  86. if (isPwdProtectedOffice && !StringUtils.hasLength(filePassword)) {
  87. // 加密文件需要密码
  88. model.addAttribute("needFilePassword", true);
  89. return EXEL_FILE_PREVIEW_PAGE;
  90. } else {
  91. if (StringUtils.hasText(outFilePath)) {
  92. try {
  93. officeToPdfService.openOfficeToPDF(filePath, outFilePath, fileAttribute);
  94. } catch (OfficeException e) {
  95. if (isPwdProtectedOffice && !OfficeUtils.isCompatible(filePath, filePassword)) {
  96. // 加密文件密码错误,提示重新输入
  97. model.addAttribute("needFilePassword", true);
  98. model.addAttribute("filePasswordError", true);
  99. return EXEL_FILE_PREVIEW_PAGE;
  100. }
  101. return otherFilePreview.notSupportedFile(model, fileAttribute, "抱歉,该文件版本不兼容,文件版本错误。");
  102. }
  103. if (isHtml) {
  104. // 对转换后的文件进行操作(改变编码方式)
  105. fileHandlerService.doActionConvertedFile(outFilePath);
  106. }
  107. //是否保留OFFICE源文件
  108. if (ConfigConstants.getDeleteSourceFile()) {
  109. KkFileUtils.deleteFileByPath(filePath);
  110. }
  111. if (isUseCached) {
  112. // 加入缓存
  113. fileHandlerService.addConvertedFile(cacheFileName, fileHandlerService.getRelativePath(outFilePath));
  114. }
  115. }
  116. }
  117. }
  118. }
  119. if (!isHtml && baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) {
  120. return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, cacheFileName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE, otherFilePreview);
  121. }
  122. cacheFileName = URLEncoder.encode(cacheFileName).replaceAll("\\+", "%20");
  123. model.addAttribute("pdfUrl", cacheFileName);
  124. return isHtml ? EXEL_FILE_PREVIEW_PAGE : PDF_FILE_PREVIEW_PAGE;
  125. }
  126. static String getPreviewType(Model model, FileAttribute fileAttribute, String officePreviewType, String baseUrl, String pdfName, String outFilePath, FileHandlerService fileHandlerService, String officePreviewTypeImage, OtherFilePreviewImpl otherFilePreview) {
  127. String suffix = fileAttribute.getSuffix();
  128. boolean isPPT = suffix.equalsIgnoreCase("ppt") || suffix.equalsIgnoreCase("pptx");
  129. List<String> imageUrls = null;
  130. try {
  131. imageUrls = fileHandlerService.pdf2jpg(outFilePath, pdfName, fileAttribute);
  132. } catch (Exception e) {
  133. Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
  134. for (Throwable throwable : throwableArray) {
  135. if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
  136. if (e.getMessage().toLowerCase().contains(OFFICE_PASSWORD_MSG)) {
  137. model.addAttribute("needFilePassword", true);
  138. return EXEL_FILE_PREVIEW_PAGE;
  139. }
  140. }
  141. }
  142. }
  143. if (imageUrls == null || imageUrls.size() < 1) {
  144. return otherFilePreview.notSupportedFile(model, fileAttribute, "office转图片异常,请联系管理员");
  145. }
  146. model.addAttribute("imgurls", imageUrls);
  147. model.addAttribute("currentUrl", imageUrls.get(0));
  148. if (officePreviewTypeImage.equals(officePreviewType)) {
  149. // PPT 图片模式使用专用预览页面
  150. return (isPPT ? PPT_FILE_PREVIEW_PAGE : OFFICE_PICTURE_FILE_PREVIEW_PAGE);
  151. } else {
  152. return PICTURE_FILE_PREVIEW_PAGE;
  153. }
  154. }
  155. }