OnlinePreviewController.java 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package cn.keking.web.controller;
  2. import cn.keking.model.FileAttribute;
  3. import cn.keking.service.FileHandlerService;
  4. import cn.keking.service.FilePreview;
  5. import cn.keking.service.FilePreviewFactory;
  6. import cn.keking.service.cache.CacheService;
  7. import cn.keking.service.impl.OtherFilePreviewImpl;
  8. import cn.keking.utils.KkFileUtils;
  9. import cn.keking.utils.WebUtils;
  10. import fr.opensagres.xdocreport.core.io.IOUtils;
  11. import io.mola.galimatias.GalimatiasParseException;
  12. import org.apache.commons.codec.binary.Base64;
  13. import org.slf4j.Logger;
  14. import org.slf4j.LoggerFactory;
  15. import org.springframework.stereotype.Controller;
  16. import org.springframework.ui.Model;
  17. import org.springframework.util.StringUtils;
  18. import org.springframework.web.bind.annotation.GetMapping;
  19. import org.springframework.web.bind.annotation.ResponseBody;
  20. import javax.servlet.http.HttpServletRequest;
  21. import javax.servlet.http.HttpServletResponse;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.net.HttpURLConnection;
  25. import java.net.URL;
  26. import java.util.Arrays;
  27. import java.util.List;
  28. import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE;
  29. /**
  30. * @author yudian-it
  31. */
  32. @Controller
  33. public class OnlinePreviewController {
  34. public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败,请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!";
  35. private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class);
  36. private final FilePreviewFactory previewFactory;
  37. private final CacheService cacheService;
  38. private final FileHandlerService fileHandlerService;
  39. private final OtherFilePreviewImpl otherFilePreview;
  40. public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, OtherFilePreviewImpl otherFilePreview) {
  41. this.previewFactory = filePreviewFactory;
  42. this.fileHandlerService = fileHandlerService;
  43. this.cacheService = cacheService;
  44. this.otherFilePreview = otherFilePreview;
  45. }
  46. @GetMapping( "/onlinePreview")
  47. public String onlinePreview(String url, Model model, HttpServletRequest req) {
  48. String fileUrl;
  49. try {
  50. fileUrl = WebUtils.decodeUrl(url);
  51. } catch (Exception ex) {
  52. String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
  53. return otherFilePreview.notSupportedFile(model, errorMsg);
  54. }
  55. FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req);
  56. model.addAttribute("file", fileAttribute);
  57. FilePreview filePreview = previewFactory.get(fileAttribute);
  58. logger.info("预览文件url:{},previewType:{}", fileUrl, fileAttribute.getType());
  59. return filePreview.filePreviewHandle(fileUrl, model, fileAttribute);
  60. }
  61. @GetMapping( "/picturesPreview")
  62. public String picturesPreview(String urls, Model model, HttpServletRequest req) {
  63. String fileUrls;
  64. try {
  65. fileUrls = WebUtils.decodeUrl(urls);
  66. // 防止XSS攻击
  67. fileUrls = KkFileUtils.htmlEscape(fileUrls);
  68. } catch (Exception ex) {
  69. String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "urls");
  70. return otherFilePreview.notSupportedFile(model, errorMsg);
  71. }
  72. logger.info("预览文件url:{},urls:{}", fileUrls, urls);
  73. // 抽取文件并返回文件列表
  74. String[] images = fileUrls.split("\\|");
  75. List<String> imgUrls = Arrays.asList(images);
  76. model.addAttribute("imgUrls", imgUrls);
  77. String currentUrl = req.getParameter("currentUrl");
  78. if (StringUtils.hasText(currentUrl)) {
  79. String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl));
  80. decodedCurrentUrl = KkFileUtils.htmlEscape(decodedCurrentUrl); // 防止XSS攻击
  81. model.addAttribute("currentUrl", decodedCurrentUrl);
  82. } else {
  83. model.addAttribute("currentUrl", imgUrls.get(0));
  84. }
  85. return PICTURE_FILE_PREVIEW_PAGE;
  86. }
  87. /**
  88. * 根据url获取文件内容
  89. * 当pdfjs读取存在跨域问题的文件时将通过此接口读取
  90. *
  91. * @param urlPath url
  92. * @param response response
  93. */
  94. @GetMapping("/getCorsFile")
  95. public void getCorsFile(String urlPath, HttpServletResponse response) throws IOException {
  96. try {
  97. urlPath = WebUtils.decodeUrl(urlPath);
  98. } catch (Exception ex) {
  99. logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath),ex);
  100. return;
  101. }
  102. HttpURLConnection urlcon = null;
  103. InputStream inputStream = null;
  104. assert urlPath != null;
  105. if (!urlPath.toLowerCase().startsWith("http") && !urlPath.toLowerCase().startsWith("https") && !urlPath.toLowerCase().startsWith("ftp")) {
  106. logger.info("读取跨域文件异常,可能存在非法访问,urlPath:{}", urlPath);
  107. return;
  108. }
  109. logger.info("下载跨域pdf文件url:{}", urlPath);
  110. if (!urlPath.toLowerCase().startsWith("ftp:")){
  111. try {
  112. URL url = WebUtils.normalizedURL(urlPath);
  113. urlcon=(HttpURLConnection)url.openConnection();
  114. urlcon.setConnectTimeout(30000);
  115. urlcon.setReadTimeout(30000);
  116. urlcon.setInstanceFollowRedirects(false);
  117. int responseCode = urlcon.getResponseCode();
  118. if (responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP) { //301 302
  119. url =new URL(urlcon.getHeaderField("Location"));
  120. urlcon=(HttpURLConnection)url.openConnection();
  121. }
  122. if (responseCode == HttpURLConnection.HTTP_NOT_FOUND ||responseCode == HttpURLConnection.HTTP_FORBIDDEN || responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR ) { //403 404 500
  123. logger.error("读取跨域文件异常,url:{},错误:{}", urlPath,responseCode);
  124. } else {
  125. if(urlPath.contains( ".svg")) {
  126. response.setContentType("image/svg+xml");
  127. }
  128. inputStream=(url).openStream();
  129. IOUtils.copy(inputStream, response.getOutputStream());
  130. }
  131. } catch (IOException | GalimatiasParseException e) {
  132. logger.error("读取跨域文件异常,url:{}", urlPath);
  133. } finally {
  134. assert urlcon != null;
  135. urlcon.disconnect();
  136. IOUtils.closeQuietly(inputStream);
  137. }
  138. } else {
  139. try {
  140. URL url = WebUtils.normalizedURL(urlPath);
  141. if(urlPath.contains(".svg")) {
  142. response.setContentType("image/svg+xml");
  143. }
  144. inputStream = (url).openStream();
  145. IOUtils.copy(inputStream, response.getOutputStream());
  146. } catch (IOException | GalimatiasParseException e) {
  147. logger.error("读取跨域文件异常,url:{}", urlPath);
  148. } finally {
  149. IOUtils.closeQuietly(inputStream);
  150. }
  151. }
  152. }
  153. /**
  154. * 通过api接口入队
  155. *
  156. * @param url 请编码后在入队
  157. */
  158. @GetMapping("/addTask")
  159. @ResponseBody
  160. public String addQueueTask(String url) {
  161. logger.info("添加转码队列url:{}", url);
  162. cacheService.addQueueTask(url);
  163. return "success";
  164. }
  165. }