mirror of
https://gitee.com/kekingcn/file-online-preview.git
synced 2026-03-14 13:13:47 +08:00
文本文档加入缓存,安全修复XSS,美化404、500报错等,新增SVG格式预览,ofd优化印章渲染兼容性 (#413)
1、文本文档加入缓存 2、安全修复XSS(跨站脚本攻击) 3、美化404、500报错等 5、新增 SVG格式预览 5、ofd优化印章渲染兼容性 Co-authored-by: gaoxiongzaq <admin@cxcp.com>
This commit is contained in:
@@ -24,14 +24,15 @@ public enum FileType {
|
||||
FLV("flvFilePreviewImpl"),
|
||||
CAD("cadFilePreviewImpl"),
|
||||
TIFF("tiffFilePreviewImpl"),
|
||||
OFD("ofdFilePreviewImpl");
|
||||
|
||||
OFD("ofdFilePreviewImpl"),
|
||||
SVG("svgFilePreviewImpl");
|
||||
|
||||
private static final String[] OFFICE_TYPES = {"docx", "wps", "doc", "docm", "xls", "xlsx", "csv" ,"xlsm", "ppt", "pptx", "vsd", "rtf", "odt", "wmf", "emf", "dps", "et", "ods", "ots", "tsv", "odp", "otp", "sxi", "ott", "vsdx", "fodt", "fods", "xltx","tga","psd"};
|
||||
private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "jfif", "webp"};
|
||||
private static final String[] ARCHIVE_TYPES = {"rar", "zip", "jar", "7-zip", "tar", "gzip", "7z"};
|
||||
private static final String[] TIFF_TYPES = {"tif", "tiff"};
|
||||
private static final String[] OFD_TYPES = {"ofd"};
|
||||
private static final String[] SVG_TYPES = {"svg"};
|
||||
private static final String[] CAD_TYPES = {"dwg", "dxf"};
|
||||
private static final String[] SSIM_TEXT_TYPES = ConfigConstants.getSimText();
|
||||
private static final String[] CODES = {"java", "c", "php", "go", "python", "py", "js", "html", "ftl", "css", "lua", "sh", "rb", "yaml", "yml", "json", "h", "cpp", "cs", "aspx", "jsp"};
|
||||
@@ -70,6 +71,9 @@ public enum FileType {
|
||||
for (String cad : CAD_TYPES) {
|
||||
FILE_TYPE_MAPPER.put(cad, FileType.CAD);
|
||||
}
|
||||
for (String svg : SVG_TYPES) {
|
||||
FILE_TYPE_MAPPER.put(svg, FileType.SVG);
|
||||
}
|
||||
FILE_TYPE_MAPPER.put("md", FileType.MARKDOWN);
|
||||
FILE_TYPE_MAPPER.put("xml", FileType.XML);
|
||||
FILE_TYPE_MAPPER.put("pdf", FileType.PDF);
|
||||
|
||||
@@ -17,6 +17,7 @@ public interface FilePreview {
|
||||
String PICTURE_FILE_PREVIEW_PAGE = "picture";
|
||||
String TIFF_FILE_PREVIEW_PAGE = "tiff";
|
||||
String OFD_FILE_PREVIEW_PAGE = "ofd";
|
||||
String SVG_FILE_PREVIEW_PAGE = "svg";
|
||||
String OFFICE_PICTURE_FILE_PREVIEW_PAGE = "officePicture";
|
||||
String TXT_FILE_PREVIEW_PAGE = "txt";
|
||||
String CODE_FILE_PREVIEW_PAGE = "code";
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.keking.service.impl;
|
||||
|
||||
import cn.keking.model.FileAttribute;
|
||||
import cn.keking.service.FilePreview;
|
||||
import cn.keking.utils.KkFileUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.ui.Model;
|
||||
|
||||
@@ -42,8 +43,8 @@ public class OtherFilePreviewImpl implements FilePreview {
|
||||
* @return 页面
|
||||
*/
|
||||
public String notSupportedFile(Model model, String fileType, String errMsg) {
|
||||
model.addAttribute("fileType", fileType);
|
||||
model.addAttribute("msg", errMsg);
|
||||
model.addAttribute("fileType", KkFileUtils.htmlEscape(fileType));
|
||||
model.addAttribute("msg", KkFileUtils.htmlEscape(errMsg));
|
||||
return NOT_SUPPORTED_FILE_PAGE;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@ package cn.keking.service.impl;
|
||||
|
||||
import cn.keking.model.FileAttribute;
|
||||
import cn.keking.model.ReturnResponse;
|
||||
import cn.keking.service.FileHandlerService;
|
||||
import cn.keking.service.FilePreview;
|
||||
import cn.keking.utils.DownloadUtils;
|
||||
import cn.keking.service.FileHandlerService;
|
||||
import cn.keking.utils.KkFileUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -28,6 +30,7 @@ public class PictureFilePreviewImpl implements FilePreview {
|
||||
|
||||
@Override
|
||||
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
||||
url= KkFileUtils.htmlEscape(url);
|
||||
List<String> imgUrls = new ArrayList<>();
|
||||
imgUrls.add(url);
|
||||
String fileKey = fileAttribute.getFileKey();
|
||||
|
||||
@@ -3,6 +3,7 @@ package cn.keking.service.impl;
|
||||
import cn.keking.config.ConfigConstants;
|
||||
import cn.keking.model.FileAttribute;
|
||||
import cn.keking.model.ReturnResponse;
|
||||
import cn.keking.service.FileHandlerService;
|
||||
import cn.keking.service.FilePreview;
|
||||
import cn.keking.utils.DownloadUtils;
|
||||
import cn.keking.utils.EncodingDetects;
|
||||
@@ -23,9 +24,11 @@ import java.nio.file.Paths;
|
||||
@Service
|
||||
public class SimTextFilePreviewImpl implements FilePreview {
|
||||
|
||||
private final FileHandlerService fileHandlerService;
|
||||
private final OtherFilePreviewImpl otherFilePreview;
|
||||
|
||||
public SimTextFilePreviewImpl(OtherFilePreviewImpl otherFilePreview) {
|
||||
public SimTextFilePreviewImpl(FileHandlerService fileHandlerService,OtherFilePreviewImpl otherFilePreview) {
|
||||
this.fileHandlerService = fileHandlerService;
|
||||
this.otherFilePreview = otherFilePreview;
|
||||
}
|
||||
private static final String FILE_DIR = ConfigConstants.getFileDir();
|
||||
@@ -33,16 +36,30 @@ public class SimTextFilePreviewImpl implements FilePreview {
|
||||
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
||||
String fileName = fileAttribute.getName();
|
||||
String filePath = FILE_DIR + fileName;
|
||||
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
|
||||
if (response.isFailure()) {
|
||||
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
|
||||
if (!fileHandlerService.listConvertedFiles().containsKey(fileName) || !ConfigConstants.isCacheEnabled()) {
|
||||
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
|
||||
if (response.isFailure()) {
|
||||
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
|
||||
}
|
||||
filePath = response.getContent();
|
||||
if (ConfigConstants.isCacheEnabled()) {
|
||||
fileHandlerService.addConvertedFile(fileName, filePath); //加入缓存
|
||||
}
|
||||
try {
|
||||
String fileData = HtmlUtils.htmlEscape(textData(filePath));
|
||||
model.addAttribute("textData", Base64.encodeBase64String(fileData.getBytes()));
|
||||
} catch (IOException e) {
|
||||
return otherFilePreview.notSupportedFile(model, fileAttribute, e.getLocalizedMessage());
|
||||
}
|
||||
return TXT_FILE_PREVIEW_PAGE;
|
||||
}
|
||||
String fileData = null;
|
||||
try {
|
||||
String fileData = HtmlUtils.htmlEscape(textData(filePath));
|
||||
model.addAttribute("textData", Base64.encodeBase64String(fileData.getBytes()));
|
||||
fileData = HtmlUtils.htmlEscape(textData(filePath));
|
||||
} catch (IOException e) {
|
||||
return otherFilePreview.notSupportedFile(model, fileAttribute, e.getLocalizedMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
model.addAttribute("textData", Base64.encodeBase64String(fileData.getBytes()));
|
||||
return TXT_FILE_PREVIEW_PAGE;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.keking.service.impl;
|
||||
|
||||
import cn.keking.model.FileAttribute;
|
||||
import cn.keking.service.FilePreview;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.ui.Model;
|
||||
|
||||
/**
|
||||
* svg 图片文件处理
|
||||
* @author kl (http://kailing.pub)
|
||||
* @since 2021/2/8
|
||||
*/
|
||||
@Service
|
||||
public class SvgFilePreviewImpl implements FilePreview {
|
||||
|
||||
private final PictureFilePreviewImpl pictureFilePreview;
|
||||
|
||||
public SvgFilePreviewImpl(PictureFilePreviewImpl pictureFilePreview) {
|
||||
this.pictureFilePreview = pictureFilePreview;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
|
||||
pictureFilePreview.filePreviewHandle(url,model,fileAttribute);
|
||||
return SVG_FILE_PREVIEW_PAGE;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package cn.keking.utils;
|
||||
import cpdetector.CharsetPrinter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.HtmlUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -115,6 +117,13 @@ public class KkFileUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static String htmlEscape(String input) {
|
||||
if(StringUtils.hasText(input)){
|
||||
return HtmlUtils.htmlEscape(input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过文件名获取文件后缀
|
||||
*
|
||||
|
||||
@@ -6,6 +6,7 @@ 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.KkFileUtils;
|
||||
import cn.keking.utils.WebUtils;
|
||||
import fr.opensagres.xdocreport.core.io.IOUtils;
|
||||
import io.mola.galimatias.GalimatiasParseException;
|
||||
@@ -17,7 +18,6 @@ import org.springframework.ui.Model;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.util.HtmlUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -53,10 +53,6 @@ public class OnlinePreviewController {
|
||||
|
||||
@GetMapping( "/onlinePreview")
|
||||
public String onlinePreview(String url, Model model, HttpServletRequest req) {
|
||||
if (url == null || url.length() == 0){
|
||||
logger.info("URL异常:{}", url);
|
||||
return otherFilePreview.notSupportedFile(model, "NULL地址不允许预览");
|
||||
}
|
||||
String fileUrl;
|
||||
try {
|
||||
fileUrl = WebUtils.decodeUrl(url);
|
||||
@@ -73,15 +69,11 @@ public class OnlinePreviewController {
|
||||
|
||||
@GetMapping( "/picturesPreview")
|
||||
public String picturesPreview(String urls, Model model, HttpServletRequest req) {
|
||||
if (urls == null || urls.length() == 0){
|
||||
logger.info("URL异常:{}", urls);
|
||||
return otherFilePreview.notSupportedFile(model, "NULL地址不允许预览");
|
||||
}
|
||||
String fileUrls;
|
||||
try {
|
||||
fileUrls = WebUtils.decodeUrl(urls);
|
||||
// 防止XSS攻击
|
||||
fileUrls = HtmlUtils.htmlEscape(fileUrls);
|
||||
fileUrls = KkFileUtils.htmlEscape(fileUrls);
|
||||
} catch (Exception ex) {
|
||||
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "urls");
|
||||
return otherFilePreview.notSupportedFile(model, errorMsg);
|
||||
@@ -94,6 +86,7 @@ public class OnlinePreviewController {
|
||||
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));
|
||||
@@ -110,13 +103,6 @@ public class OnlinePreviewController {
|
||||
*/
|
||||
@GetMapping("/getCorsFile")
|
||||
public void getCorsFile(String urlPath, HttpServletResponse response) throws IOException {
|
||||
if (urlPath == null || urlPath.length() == 0){
|
||||
logger.info("URL异常:{}", urlPath);
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
response.setHeader("Content-Type", "text/html; charset=UTF-8");
|
||||
response.getWriter().println("NULL地址不允许预览");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
urlPath = WebUtils.decodeUrl(urlPath);
|
||||
} catch (Exception ex) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package cn.keking.web.filter;
|
||||
|
||||
import cn.keking.config.ConfigConstants;
|
||||
import cn.keking.config.WatermarkConfigConstants;
|
||||
import cn.keking.utils.KkFileUtils;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -46,7 +47,7 @@ public class AttributeSetFilter implements Filter {
|
||||
* @param request request
|
||||
*/
|
||||
private void setWatermarkAttribute(ServletRequest request) {
|
||||
String watermarkTxt = request.getParameter("watermarkTxt");
|
||||
String watermarkTxt= KkFileUtils.htmlEscape(request.getParameter("watermarkTxt"));
|
||||
request.setAttribute("watermarkTxt", watermarkTxt != null ? watermarkTxt : WatermarkConfigConstants.getWatermarkTxt());
|
||||
String watermarkXSpace = request.getParameter("watermarkXSpace");
|
||||
request.setAttribute("watermarkXSpace", watermarkXSpace != null ? watermarkXSpace : WatermarkConfigConstants.getWatermarkXSpace());
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.*;
|
||||
import java.io.IOException;
|
||||
@@ -55,6 +56,9 @@ public class TrustDirFilter implements Filter {
|
||||
}
|
||||
|
||||
private boolean allowPreview(String urlPath) {
|
||||
if(!StringUtils.hasText(urlPath)){
|
||||
return false ;
|
||||
}
|
||||
try {
|
||||
URL url = WebUtils.normalizedURL(urlPath);
|
||||
if ("file".equals(url.getProtocol().toLowerCase(Locale.ROOT))) {
|
||||
|
||||
Reference in New Issue
Block a user