FileHandlerService.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. package cn.keking.service;
  2. import cn.keking.config.ConfigConstants;
  3. import cn.keking.model.FileAttribute;
  4. import cn.keking.model.FileType;
  5. import cn.keking.service.cache.CacheService;
  6. import cn.keking.service.cache.NotResourceCache;
  7. import cn.keking.utils.EncodingDetects;
  8. import cn.keking.utils.KkFileUtils;
  9. import cn.keking.utils.WebUtils;
  10. import cn.keking.web.filter.BaseUrlFilter;
  11. import com.aspose.cad.*;
  12. import com.aspose.cad.fileformats.tiff.enums.TiffExpectedFormat;
  13. import com.aspose.cad.imageoptions.CadRasterizationOptions;
  14. import com.aspose.cad.imageoptions.PdfOptions;
  15. import com.aspose.cad.imageoptions.SvgOptions;
  16. import com.aspose.cad.imageoptions.TiffOptions;
  17. import com.aspose.cad.internal.Exceptions.TimeoutException;
  18. import com.itextpdf.text.pdf.PdfReader;
  19. import org.apache.commons.lang3.exception.ExceptionUtils;
  20. import org.apache.pdfbox.pdmodel.PDDocument;
  21. import org.apache.pdfbox.rendering.ImageType;
  22. import org.apache.pdfbox.rendering.PDFRenderer;
  23. import org.apache.pdfbox.tools.imageio.ImageIOUtil;
  24. import org.apache.poi.EncryptedDocumentException;
  25. import org.slf4j.Logger;
  26. import org.slf4j.LoggerFactory;
  27. import org.springframework.beans.factory.annotation.Value;
  28. import org.springframework.stereotype.Component;
  29. import org.springframework.util.CollectionUtils;
  30. import org.springframework.util.StringUtils;
  31. import javax.servlet.http.HttpServletRequest;
  32. import java.awt.image.BufferedImage;
  33. import java.io.*;
  34. import java.net.URLEncoder;
  35. import java.nio.charset.StandardCharsets;
  36. import java.util.ArrayList;
  37. import java.util.List;
  38. import java.util.Map;
  39. import java.util.Objects;
  40. import java.util.concurrent.*;
  41. import java.util.stream.IntStream;
  42. /**
  43. * @author yudian-it
  44. * @date 2017/11/13
  45. */
  46. @Component
  47. public class FileHandlerService {
  48. private static final String PDF2JPG_IMAGE_FORMAT = ".jpg";
  49. private static final String PDF_PASSWORD_MSG = "password";
  50. private final Logger logger = LoggerFactory.getLogger(FileHandlerService.class);
  51. private final String fileDir = ConfigConstants.getFileDir();
  52. private final CacheService cacheService;
  53. @Value("${server.tomcat.uri-encoding:UTF-8}")
  54. private String uriEncoding;
  55. public FileHandlerService(CacheService cacheService) {
  56. this.cacheService = cacheService;
  57. }
  58. /**
  59. * @return 已转换过的文件集合(缓存)
  60. */
  61. public Map<String, String> listConvertedFiles() {
  62. return cacheService.getPDFCache();
  63. }
  64. /**
  65. * @return 已转换过的文件,根据文件名获取
  66. */
  67. public String getConvertedFile(String key) {
  68. return cacheService.getPDFCache(key);
  69. }
  70. /**
  71. * @param key pdf本地路径
  72. * @return 已将pdf转换成图片的图片本地相对路径
  73. */
  74. public Integer getPdf2jpgCache(String key) {
  75. return cacheService.getPdfImageCache(key);
  76. }
  77. /**
  78. * 从路径中获取文件负
  79. *
  80. * @param path 类似这种:C:\Users\yudian-it\Downloads
  81. * @return 文件名
  82. */
  83. public String getFileNameFromPath(String path) {
  84. return path.substring(path.lastIndexOf(File.separator) + 1);
  85. }
  86. /**
  87. * 获取相对路径
  88. *
  89. * @param absolutePath 绝对路径
  90. * @return 相对路径
  91. */
  92. public String getRelativePath(String absolutePath) {
  93. return absolutePath.substring(fileDir.length());
  94. }
  95. /**
  96. * 添加转换后PDF缓存
  97. *
  98. * @param fileName pdf文件名
  99. * @param value 缓存相对路径
  100. */
  101. public void addConvertedFile(String fileName, String value) {
  102. cacheService.putPDFCache(fileName, value);
  103. }
  104. /**
  105. * 添加转换后图片组缓存
  106. *
  107. * @param pdfFilePath pdf文件绝对路径
  108. * @param num 图片张数
  109. */
  110. public void addPdf2jpgCache(String pdfFilePath, int num) {
  111. cacheService.putPdfImageCache(pdfFilePath, num);
  112. }
  113. /**
  114. * 获取redis中压缩包内图片文件
  115. *
  116. * @param fileKey fileKey
  117. * @return 图片文件访问url列表
  118. */
  119. public List<String> getImgCache(String fileKey) {
  120. return cacheService.getImgCache(fileKey);
  121. }
  122. /**
  123. * 设置redis中压缩包内图片文件
  124. *
  125. * @param fileKey fileKey
  126. * @param imgs 图片文件访问url列表
  127. */
  128. public void putImgCache(String fileKey, List<String> imgs) {
  129. cacheService.putImgCache(fileKey, imgs);
  130. }
  131. /**
  132. cad定义线程池
  133. */
  134. private static final ExecutorService pool = Executors.newFixedThreadPool(ConfigConstants.getCadThread());
  135. /**
  136. * 对转换后的文件进行操作(改变编码方式)
  137. *
  138. * @param outFilePath 文件绝对路径
  139. */
  140. public void doActionConvertedFile(String outFilePath) {
  141. String charset = EncodingDetects.getJavaEncode(outFilePath);
  142. StringBuilder sb = new StringBuilder();
  143. try (InputStream inputStream = new FileInputStream(outFilePath);
  144. BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset))) {
  145. String line;
  146. while (null != (line = reader.readLine())) {
  147. if (line.contains("charset=gb2312")) {
  148. line = line.replace("charset=gb2312", "charset=utf-8");
  149. }
  150. sb.append(line);
  151. }
  152. // 添加sheet控制头
  153. sb.append("<script src=\"js/jquery-3.6.1.min.js\" type=\"text/javascript\"></script>");
  154. sb.append("<script src=\"js/excel.header.js\" type=\"text/javascript\"></script>");
  155. sb.append("<link rel=\"stylesheet\" href=\"bootstrap/css/xlsx.css\">");
  156. } catch (IOException e) {
  157. e.printStackTrace();
  158. }
  159. // 重新写入文件
  160. try (FileOutputStream fos = new FileOutputStream(outFilePath);
  161. BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
  162. writer.write(sb.toString());
  163. } catch (IOException e) {
  164. e.printStackTrace();
  165. }
  166. }
  167. /**
  168. * 获取本地 pdf 转 image 后的 web 访问地址
  169. * @param pdfName pdf文件名
  170. * @param index 图片索引
  171. * @return 图片访问地址
  172. */
  173. private String getPdf2jpgUrl(String pdfName, int index) {
  174. String baseUrl = BaseUrlFilter.getBaseUrl();
  175. String pdfFolder = pdfName.substring(0, pdfName.length() - 4);
  176. String urlPrefix;
  177. try {
  178. urlPrefix = baseUrl + URLEncoder.encode(pdfFolder, uriEncoding).replaceAll("\\+", "%20");
  179. } catch (UnsupportedEncodingException e) {
  180. logger.error("UnsupportedEncodingException", e);
  181. urlPrefix = baseUrl + pdfFolder;
  182. }
  183. return urlPrefix + "/" + index + PDF2JPG_IMAGE_FORMAT;
  184. }
  185. /**
  186. * 获取缓存中的 pdf 转换成 jpg 图片集
  187. * @param pdfFilePath pdf文件路径
  188. * @param pdfName pdf文件名称
  189. * @return 图片访问集合
  190. */
  191. private List<String> loadPdf2jpgCache(String pdfFilePath, String pdfName) {
  192. List<String> imageUrls = new ArrayList<>();
  193. Integer imageCount = this.getPdf2jpgCache(pdfFilePath);
  194. if (Objects.isNull(imageCount)) {
  195. return imageUrls;
  196. }
  197. IntStream.range(0, imageCount).forEach(i -> {
  198. String imageUrl = this.getPdf2jpgUrl(pdfName, i);
  199. imageUrls.add(imageUrl);
  200. });
  201. return imageUrls;
  202. }
  203. /**
  204. * pdf文件转换成jpg图片集
  205. *
  206. * @param pdfFilePath pdf文件路径
  207. * @param pdfName pdf文件名称
  208. * @return 图片访问集合
  209. */
  210. public List<String> pdf2jpg(String pdfFilePath, String pdfName, FileAttribute fileAttribute) throws Exception {
  211. boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
  212. String filePassword = fileAttribute.getFilePassword();
  213. String pdfPassword = null;
  214. PDDocument doc = null;
  215. PdfReader pdfReader = null;
  216. if (!forceUpdatedCache) {
  217. List<String> cacheResult = this.loadPdf2jpgCache(pdfFilePath, pdfName);
  218. if (!CollectionUtils.isEmpty(cacheResult)) {
  219. return cacheResult;
  220. }
  221. }
  222. List<String> imageUrls = new ArrayList<>();
  223. try {
  224. File pdfFile = new File(pdfFilePath);
  225. if (!pdfFile.exists()) {
  226. return null;
  227. }
  228. doc = PDDocument.load(pdfFile,filePassword);
  229. doc.setResourceCache(new NotResourceCache());
  230. int pageCount = doc.getNumberOfPages();
  231. PDFRenderer pdfRenderer = new PDFRenderer(doc);
  232. int index = pdfFilePath.lastIndexOf(".");
  233. String folder = pdfFilePath.substring(0, index);
  234. File path = new File(folder);
  235. if (!path.exists() && !path.mkdirs()) {
  236. logger.error("创建转换文件【{}】目录失败,请检查目录权限!", folder);
  237. }
  238. String imageFilePath;
  239. for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
  240. imageFilePath = folder + File.separator + pageIndex + PDF2JPG_IMAGE_FORMAT;
  241. BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, ConfigConstants.getPdf2JpgDpi(), ImageType.RGB);
  242. ImageIOUtil.writeImage(image, imageFilePath, ConfigConstants.getPdf2JpgDpi());
  243. String imageUrl = this.getPdf2jpgUrl(pdfName, pageIndex);
  244. imageUrls.add(imageUrl);
  245. }
  246. try {
  247. pdfReader = new PdfReader(pdfFilePath); //读取PDF文件
  248. } catch (Exception e) { //获取异常方法 判断是否有加密字符串
  249. Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
  250. for (Throwable throwable : throwableArray) {
  251. if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
  252. if (e.getMessage().toLowerCase().contains(PDF_PASSWORD_MSG)) {
  253. pdfPassword = PDF_PASSWORD_MSG;
  254. }
  255. }
  256. }
  257. logger.error("Convert pdf exception, pdfFilePath:{}", pdfFilePath, e);
  258. } finally {
  259. if (pdfReader != null) { //关闭
  260. pdfReader.close();
  261. }
  262. }
  263. //判断是否加密文件 加密文件不缓存
  264. if (!PDF_PASSWORD_MSG.equals(pdfPassword)) {
  265. this.addPdf2jpgCache(pdfFilePath, pageCount);
  266. }
  267. } catch (IOException e) {
  268. logger.error("Convert pdf to jpg exception, pdfFilePath:{}", pdfFilePath, e);
  269. throw new Exception(e);
  270. } finally {
  271. if (doc != null) { //关闭
  272. doc.close();
  273. }
  274. }
  275. return imageUrls;
  276. }
  277. /**
  278. * cad文件转pdf
  279. *
  280. * @param inputFilePath cad文件路径
  281. * @param outputFilePath pdf输出文件路径
  282. * @return 转换是否成功
  283. */
  284. public String cadToPdf(String inputFilePath, String outputFilePath ,String cadPreviewType) throws Exception {
  285. final InterruptionTokenSource source = new InterruptionTokenSource();//CAD延时
  286. Callable<String> call = () -> {
  287. File outputFile = new File(outputFilePath);
  288. LoadOptions opts = new LoadOptions();
  289. opts.setSpecifiedEncoding(CodePages.SimpChinese);
  290. Image cadImage = Image.load(inputFilePath, opts);
  291. CadRasterizationOptions cadRasterizationOptions = new CadRasterizationOptions();
  292. cadRasterizationOptions.setBackgroundColor(Color.getWhite());
  293. cadRasterizationOptions.setPageWidth(1400);
  294. cadRasterizationOptions.setPageHeight(650);
  295. cadRasterizationOptions.setAutomaticLayoutsScaling(true);
  296. cadRasterizationOptions.setNoScaling(false);
  297. cadRasterizationOptions.setDrawType(1);
  298. SvgOptions SvgOptions = null;
  299. PdfOptions pdfOptions = null;
  300. TiffOptions TiffOptions = null;
  301. switch (cadPreviewType) { //新增格式方法
  302. case "svg":
  303. SvgOptions = new SvgOptions();
  304. SvgOptions.setVectorRasterizationOptions(cadRasterizationOptions);
  305. SvgOptions.setInterruptionToken(source.getToken());
  306. break;
  307. case "pdf":
  308. pdfOptions = new PdfOptions();
  309. pdfOptions.setVectorRasterizationOptions(cadRasterizationOptions);
  310. pdfOptions.setInterruptionToken(source.getToken());
  311. break;
  312. case "tif":
  313. TiffOptions = new TiffOptions(TiffExpectedFormat.TiffJpegRgb);
  314. TiffOptions.setVectorRasterizationOptions(cadRasterizationOptions);
  315. TiffOptions.setInterruptionToken(source.getToken());
  316. break;
  317. }
  318. try (OutputStream stream = new FileOutputStream(outputFile)) {
  319. switch (cadPreviewType) {
  320. case "svg":
  321. cadImage.save(stream, SvgOptions);
  322. break;
  323. case "pdf":
  324. cadImage.save(stream, pdfOptions);
  325. break;
  326. case "tif":
  327. cadImage.save(stream, TiffOptions);
  328. break;
  329. }
  330. } catch (IOException e) {
  331. logger.error("PDFFileNotFoundException,inputFilePath:{}", inputFilePath, e);
  332. return null;
  333. } finally {
  334. //关闭
  335. if (cadImage != null) { //关闭
  336. cadImage.dispose();
  337. }
  338. source.interrupt(); //结束任务
  339. }
  340. return "true";
  341. };
  342. Future<String> result = pool.submit(call);
  343. try {
  344. // 如果在超时时间内,没有数据返回:则抛出TimeoutException异常
  345. result.get(Long.parseLong(ConfigConstants.getCadTimeout()), TimeUnit.SECONDS);
  346. } catch (InterruptedException e) {
  347. System.out.println("InterruptedException发生");
  348. return null;
  349. } catch (ExecutionException e) {
  350. System.out.println("ExecutionException发生");
  351. return null;
  352. } catch (TimeoutException e) {
  353. System.out.println("TimeoutException发生,意味着线程超时报错");
  354. return null;
  355. } finally {
  356. source.dispose();
  357. }
  358. return "true";
  359. }
  360. /**
  361. *
  362. * @param str 原字符串(待截取原串)
  363. * @param posStr 指定字符串
  364. * @return 截取截取指定字符串之后的数据
  365. */
  366. public static String getSubString(String str, String posStr){
  367. return str.substring(str.indexOf(posStr) + posStr.length());
  368. }
  369. /**
  370. * 获取文件属性
  371. *
  372. * @param url url
  373. * @return 文件属性
  374. */
  375. public FileAttribute getFileAttribute(String url, HttpServletRequest req) {
  376. FileAttribute attribute = new FileAttribute();
  377. String suffix;
  378. FileType type;
  379. String fileName;
  380. String fullFileName = WebUtils.getUrlParameterReg(url, "fullfilename");
  381. if (StringUtils.hasText(fullFileName)) {
  382. fileName = fullFileName;
  383. type = FileType.typeFromFileName(fullFileName);
  384. suffix = KkFileUtils.suffixFromFileName(fullFileName);
  385. // 移除fullfilename参数
  386. if (url.indexOf("fullfilename=" + fullFileName + "&") > 0) {
  387. url = url.replace("fullfilename=" + fullFileName + "&", "");
  388. } else {
  389. url = url.replace("fullfilename=" + fullFileName, "");
  390. }
  391. } else {
  392. fileName = WebUtils.getFileNameFromURL(url);
  393. type = FileType.typeFromUrl(url);
  394. suffix = WebUtils.suffixFromUrl(url);
  395. }
  396. if (url.contains("?fileKey=")) {
  397. String[] strs = url.split("="); //处理解压后有反代情况下 文件的路径
  398. String urlStrr = getSubString(url, strs[1]);
  399. urlStrr = urlStrr.substring(0,urlStrr.lastIndexOf("?"));
  400. fileName = strs[1] + urlStrr.trim();
  401. attribute.setSkipDownLoad(true);
  402. }
  403. url = WebUtils.encodeUrlFileName(url);
  404. fileName = KkFileUtils.htmlEscape(fileName); //文件名处理
  405. attribute.setType(type);
  406. attribute.setName(fileName);
  407. attribute.setSuffix(suffix);
  408. attribute.setUrl(url);
  409. if (req != null) {
  410. String officePreviewType = req.getParameter("officePreviewType");
  411. String forceUpdatedCache = req.getParameter("forceUpdatedCache");
  412. String fileKey = WebUtils.getUrlParameterReg(url, "fileKey");
  413. if (StringUtils.hasText(officePreviewType)) {
  414. attribute.setOfficePreviewType(officePreviewType);
  415. }
  416. if (StringUtils.hasText(fileKey)) {
  417. attribute.setFileKey(fileKey);
  418. }
  419. if ("true".equalsIgnoreCase(forceUpdatedCache)) {
  420. attribute.setForceUpdatedCache(true);
  421. }
  422. String tifPreviewType = req.getParameter("tifPreviewType");
  423. if (StringUtils.hasText(tifPreviewType)) {
  424. attribute.setTifPreviewType(tifPreviewType);
  425. }
  426. String filePassword = req.getParameter("filePassword");
  427. if (StringUtils.hasText(filePassword)) {
  428. attribute.setFilePassword(filePassword);
  429. }
  430. String userToken = req.getParameter("userToken");
  431. if (StringUtils.hasText(userToken)) {
  432. attribute.setUserToken(userToken);
  433. }
  434. }
  435. return attribute;
  436. }
  437. /**
  438. * @return 已转换过的视频文件集合(缓存)
  439. */
  440. public Map<String, String> listConvertedMedias() {
  441. return cacheService.getMediaConvertCache();
  442. }
  443. /**
  444. * 添加转换后的视频文件缓存
  445. *
  446. * @param fileName
  447. * @param value
  448. */
  449. public void addConvertedMedias(String fileName, String value) {
  450. cacheService.putMediaConvertCache(fileName, value);
  451. }
  452. /**
  453. * @return 已转换视频文件缓存,根据文件名获取
  454. */
  455. public String getConvertedMedias(String key) {
  456. return cacheService.getMediaConvertCache(key);
  457. }
  458. }