Files
file-online-preview/server/src/main/java/cn/keking/web/controller/OnlinePreviewController.java

245 lines
11 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cn.keking.web.controller;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.service.FileHandlerService;
import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory;
import cn.keking.service.cache.CacheService;
import cn.keking.service.impl.OtherFilePreviewImpl;
import cn.keking.utils.*;
import cn.keking.web.filter.TrustDirFilter;
import cn.keking.web.filter.TrustHostFilter;
import fr.opensagres.xdocreport.core.io.IOUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE;
import static cn.keking.utils.KkFileUtils.isFtpUrl;
import static cn.keking.utils.KkFileUtils.isHttpUrl;
/**
* @author yudian-it
*/
@Controller
public class OnlinePreviewController {
private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class);
public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!";
private static final String ILLEGAL_ACCESS_MSG = "访问不合法:访问密码不正确";
private static final String INTERFACE_CLOSED_MSG = "接口关闭,禁止访问!";
private static final String URL_PARAM_FTP_USERNAME = "ftp.username";
private static final String URL_PARAM_FTP_PASSWORD = "ftp.password";
private static final String URL_PARAM_FTP_CONTROL_ENCODING = "ftp.control.encoding";
private static final String URL_PARAM_FTP_PORT = "ftp.control.port";
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FileHandlerService fileHandlerService, CacheService cacheService, OtherFilePreviewImpl otherFilePreview) {
this.previewFactory = filePreviewFactory;
this.fileHandlerService = fileHandlerService;
this.cacheService = cacheService;
this.otherFilePreview = otherFilePreview;
}
@GetMapping( "/onlinePreview")
public String onlinePreview(@RequestParam String url,
@RequestParam(required = false) String key,
@RequestParam(required = false) String encryption,
@RequestParam(defaultValue = "false") String highlightall,
@RequestParam(defaultValue = "0") String page,
@RequestParam(defaultValue = "false") String kkagent,
Model model,
HttpServletRequest req) {
// 验证访问权限
if (WebUtils.validateKey(key)) {
return otherFilePreview.notSupportedFile(model, ILLEGAL_ACCESS_MSG);
}
String fileUrl;
try {
fileUrl = WebUtils.decodeUrl(url, encryption);
} catch (Exception ex) {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
return otherFilePreview.notSupportedFile(model, errorMsg);
}
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req);
highlightall= KkFileUtils.htmlEscape(highlightall);
model.addAttribute("highlightall", highlightall);
model.addAttribute("page", page);
model.addAttribute("kkagent", kkagent);
model.addAttribute("file", fileAttribute);
FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType());
fileUrl =WebUtils.urlEncoderencode(fileUrl);
if (ObjectUtils.isEmpty(fileUrl)) {
return otherFilePreview.notSupportedFile(model, "非法路径,不允许访问");
}
return filePreview.filePreviewHandle(fileUrl, model, fileAttribute); //统一在这里处理 url
}
@GetMapping( "/picturesPreview")
public String picturesPreview(@RequestParam String urls,
@RequestParam(required = false) String key,
@RequestParam(required = false) String encryption,
Model model,
HttpServletRequest req) {
// 1. 验证接口是否开启
if (!ConfigConstants.getPicturesPreview()) {
return otherFilePreview.notSupportedFile(model, INTERFACE_CLOSED_MSG);
}
//2. 验证访问权限
if (WebUtils.validateKey(key)) {
return otherFilePreview.notSupportedFile(model, ILLEGAL_ACCESS_MSG);
}
String fileUrls;
try {
fileUrls = WebUtils.decodeUrl(urls, encryption);
// 防止XSS攻击
fileUrls = KkFileUtils.htmlEscape(fileUrls);
} catch (Exception ex) {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "urls");
return otherFilePreview.notSupportedFile(model, errorMsg);
}
logger.info("预览文件url{}urls{}", fileUrls, urls);
// 抽取文件并返回文件列表
String[] images = fileUrls.split("\\|");
List<String> imgUrls = Arrays.asList(images);
model.addAttribute("imgUrls", imgUrls);
String currentUrl = req.getParameter("currentUrl");
if (StringUtils.hasText(currentUrl)) {
String decodedCurrentUrl = new String(Base64.decodeBase64(currentUrl));
decodedCurrentUrl = KkFileUtils.htmlEscape(decodedCurrentUrl); // 防止XSS攻击
model.addAttribute("currentUrl", decodedCurrentUrl);
} else {
model.addAttribute("currentUrl", imgUrls.get(0));
}
return PICTURE_FILE_PREVIEW_PAGE;
}
/**
* 根据url获取文件内容
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取
*
* @param urlPath url
* @param response response
*/
@GetMapping("/getCorsFile")
public void getCorsFile(@RequestParam String urlPath,
@RequestParam(required = false) String key,
HttpServletResponse response,
HttpServletRequest req,
@RequestParam(required = false) String encryption) throws Exception {
// 1. 验证接口是否开启
if (!ConfigConstants.getGetCorsFile()) {
logger.info("接口关闭,禁止访问!url{}", urlPath);
return;
}
//2. 验证访问权限
if (WebUtils.validateKey(key)) {
logger.info("访问不合法:访问密码不正确!url{}", urlPath);
return;
}
URL url;
try {
urlPath = WebUtils.decodeUrl(urlPath, encryption);
url = WebUtils.normalizedURL(urlPath);
} catch (Exception ex) {
logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath),ex);
return;
}
assert urlPath != null;
if (!isHttpUrl(url) && !isFtpUrl(url)) {
logger.info("读取跨域文件异常可能存在非法访问urlPath{}", urlPath);
return;
}
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(urlPath, req);
InputStream inputStream = null;
logger.info("读取跨域文件url{}", urlPath);
if (!isFtpUrl(url)) {
CloseableHttpClient httpClient = HttpRequestUtils.createConfiguredHttpClient();
HttpRequestUtils.executeHttpRequest(url, httpClient, fileAttribute, responseWrapper -> IOUtils.copy(responseWrapper.getInputStream(), response.getOutputStream()));
} else {
try {
String filename = urlPath.substring(urlPath.lastIndexOf('/') + 1);
String contentType = WebUtils.getContentTypeByFilename(filename);
if (contentType != null) {
response.setContentType(contentType);
}
String ftpUsername = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_USERNAME);
String ftpPassword = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_CONTROL_ENCODING);
String support = WebUtils.getUrlParameterReg(urlPath, URL_PARAM_FTP_PORT);
inputStream= FtpUtils.preview(urlPath,support, urlPath, ftpUsername, ftpPassword, ftpControlEncoding);
IOUtils.copy(inputStream, response.getOutputStream());
} catch (IOException e) {
logger.error("读取跨域文件异常url{}", urlPath);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
}
/**
* 通过api接口入队
*
* @param url 请编码后在入队
*/
@GetMapping("/addTask")
@ResponseBody
public String addQueueTask(@RequestParam String url,
@RequestParam(required = false) String key,
@RequestParam(required = false) String encryption) {
// 1. 验证接口是否开启
if (!ConfigConstants.getAddTask()) {
String errorMsg = "接口关闭,禁止访问!";
logger.info("{}url{}", errorMsg, url);
return errorMsg;
}
String fileUrls;
try {
fileUrls = WebUtils.decodeUrl(url, encryption);
} catch (Exception ex) {
String errorMsg = "Url解析错误";
logger.info("{}url{}", errorMsg, url);
return errorMsg;
}
//2. 验证访问权限
if (WebUtils.validateKey(key)) {
String errorMsg = "访问不合法:访问密码不正确!";
logger.info("{}url{}", errorMsg, fileUrls);
return errorMsg;
}
if (!TrustHostFilter.isTrustedSourceUrl(fileUrls) || !TrustDirFilter.isTrustedFileUrl(fileUrls)) {
String errorMsg = "访问不合法:来源地址不受信任!";
logger.info("{}url{}", errorMsg, fileUrls);
return errorMsg;
}
logger.info("添加转码队列url{}", fileUrls);
cacheService.addQueueTask(fileUrls);
return "success";
}
}