This commit is contained in:
Jane
2024-07-16 15:39:04 +08:00
parent 31e21120b3
commit 8f4ec86367
1534 changed files with 968190 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
package cn.keking;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.util.StopWatch;
@SpringBootApplication
@EnableScheduling
@ComponentScan(value = "cn.keking.*")
public class ServerMain {
private static final Logger logger = LoggerFactory.getLogger(ServerMain.class);
public static void main(String[] args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = new SpringApplicationBuilder(ServerMain.class)
.logStartupInfo(false)
.run(args);
stopWatch.stop();
ServerProperties serverProperties = context.getBean(ServerProperties.class);
Integer port = serverProperties.getPort();
ServerProperties.Servlet servlet = serverProperties.getServlet();
String contextPath = servlet.getContextPath();
String urlSuffix = StringUtils.isBlank(contextPath)? String.valueOf(port):port+contextPath;
logger.info("kkFileView 服务启动完成,耗时:{}s演示页请访问: http://127.0.0.1:{} ", stopWatch.getTotalTimeSeconds(), urlSuffix);
}
}

View File

@@ -0,0 +1,748 @@
package cn.keking.config;
import cn.keking.utils.ConfigUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @author: chenjh
* @since: 2019/4/10 17:22
*/
@Component(value = ConfigConstants.BEAN_NAME)
public class ConfigConstants {
public static final String BEAN_NAME = "configConstants";
static {
//pdfbox兼容低版本jdk
System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
}
private static Boolean cacheEnabled;
private static String[] simTexts = {};
private static String[] medias = {};
private static String[] convertMedias = {};
private static String mediaConvertDisable;
private static String officePreviewType;
private static String officePreviewSwitchDisabled;
private static String ftpUsername;
private static String ftpPassword;
private static String ftpControlEncoding;
private static String baseUrl;
private static String fileDir = ConfigUtils.getHomePath() + File.separator + "file" + File.separator;
private static String localPreviewDir;
private static CopyOnWriteArraySet<String> trustHostSet;
private static CopyOnWriteArraySet<String> notTrustHostSet;
private static String pdfPresentationModeDisable;
private static String pdfDisableEditing;
private static String pdfOpenFileDisable;
private static String pdfPrintDisable;
private static String pdfDownloadDisable;
private static String pdfBookmarkDisable;
private static Boolean fileUploadDisable;
private static String tifPreviewType;
private static String beian;
private static String[] prohibit = {};
private static String size;
private static String password;
private static int pdf2JpgDpi;
private static String officeTypeWeb;
private static String cadPreviewType;
private static Boolean deleteSourceFile;
private static Boolean deleteCaptcha;
private static String officePageRange;
private static String officeWatermark;
private static String officeQuality;
private static String officeMaxImageResolution;
private static Boolean officeExportBookmarks;
private static Boolean officeExportNotes;
private static Boolean officeDocumentOpenPasswords;
private static String cadTimeout;
private static int cadThread;
private static String homePageNumber;
private static String homePagination;
private static String homePageSize;
private static String homeSearch;
public static final String DEFAULT_CACHE_ENABLED = "true";
public static final String DEFAULT_TXT_TYPE = "txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd,xbrl";
public static final String DEFAULT_MEDIA_TYPE = "mp3,wav,mp4,flv";
public static final String DEFAULT_OFFICE_PREVIEW_TYPE = "image";
public static final String DEFAULT_OFFICE_PREVIEW_SWITCH_DISABLED = "false";
public static final String DEFAULT_FTP_USERNAME = null;
public static final String DEFAULT_FTP_PASSWORD = null;
public static final String DEFAULT_FTP_CONTROL_ENCODING = "UTF-8";
public static final String DEFAULT_VALUE = "default";
public static final String DEFAULT_PDF_PRESENTATION_MODE_DISABLE = "true";
public static final String DEFAULT_PDF_OPEN_FILE_DISABLE = "true";
public static final String DEFAULT_PDF_PRINT_DISABLE = "true";
public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true";
public static final String DEFAULT_PDF_BOOKMARK_DISABLE = "true";
public static final String DEFAULT_PDF_DISABLE_EDITING = "true";
public static final String DEFAULT_FILE_UPLOAD_DISABLE = "false";
public static final String DEFAULT_TIF_PREVIEW_TYPE = "tif";
public static final String DEFAULT_CAD_PREVIEW_TYPE = "pdf";
public static final String DEFAULT_BEIAN = "";
public static final String DEFAULT_SIZE = "500MB";
public static final String DEFAULT_PROHIBIT = "exe,dll";
public static final String DEFAULT_PASSWORD = "123456";
public static final String DEFAULT_PDF2_JPG_DPI = "105";
public static final String DEFAULT_OFFICE_TYPE_WEB = "web";
public static final String DEFAULT_DELETE_SOURCE_FILE = "true";
public static final String DEFAULT_DELETE_CAPTCHA = "false";
public static final String DEFAULT_CAD_TIMEOUT = "90";
public static final String DEFAULT_CAD_THREAD = "5";
public static final String DEFAULT_OFFICE_PAQERANQE = "false";
public static final String DEFAULT_OFFICE_WATERMARK = "false";
public static final String DEFAULT_OFFICE_QUALITY = "80";
public static final String DEFAULT_OFFICE_MAXIMAQERESOLUTION = "150";
public static final String DEFAULT_OFFICE_EXPORTBOOKMARKS = "true";
public static final String DEFAULT_OFFICE_EXPORTNOTES = "true";
public static final String DEFAULT_OFFICE_EOCUMENTOPENPASSWORDS = "true";
public static final String DEFAULT_HOME_PAGENUMBER = "1";
public static final String DEFAULT_HOME_PAGINATION = "true";
public static final String DEFAULT_HOME_PAGSIZE = "15";
public static final String DEFAULT_HOME_SEARCH = "true";
public static Boolean isCacheEnabled() {
return cacheEnabled;
}
@Value("${cache.enabled:true}")
public void setCacheEnabled(String cacheEnabled) {
setCacheEnabledValueValue(Boolean.parseBoolean(cacheEnabled));
}
public static void setCacheEnabledValueValue(Boolean cacheEnabled) {
ConfigConstants.cacheEnabled = cacheEnabled;
}
public static String[] getSimText() {
return simTexts;
}
@Value("${simText:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd,xbrl}")
public void setSimText(String simText) {
String[] simTextArr = simText.split(",");
setSimTextValue(simTextArr);
}
public static void setSimTextValue(String[] simText) {
ConfigConstants.simTexts = simText;
}
public static String[] getMedia() {
return medias;
}
@Value("${media:mp3,wav,mp4,flv}")
public void setMedia(String media) {
String[] mediaArr = media.split(",");
setMediaValue(mediaArr);
}
public static void setMediaValue(String[] Media) {
ConfigConstants.medias = Media;
}
public static String[] getConvertMedias() {
return convertMedias;
}
@Value("${convertMedias:avi,mov,wmv,mkv,3gp,rm}")
public void setConvertMedias(String convertMedia) {
String[] mediaArr = convertMedia.split(",");
setConvertMediaValue(mediaArr);
}
public static void setConvertMediaValue(String[] ConvertMedia) {
ConfigConstants.convertMedias = ConvertMedia;
}
public static String getMediaConvertDisable() {
return mediaConvertDisable;
}
@Value("${media.convert.disable:true}")
public void setMediaConvertDisable(String mediaConvertDisable) {
setMediaConvertDisableValue(mediaConvertDisable);
}
public static void setMediaConvertDisableValue(String mediaConvertDisable) {
ConfigConstants.mediaConvertDisable = mediaConvertDisable;
}
public static String getOfficePreviewType() {
return officePreviewType;
}
@Value("${office.preview.type:image}")
public void setOfficePreviewType(String officePreviewType) {
setOfficePreviewTypeValue(officePreviewType);
}
public static void setOfficePreviewTypeValue(String officePreviewType) {
ConfigConstants.officePreviewType = officePreviewType;
}
public static String getFtpUsername() {
return ftpUsername;
}
@Value("${ftp.username:}")
public void setFtpUsername(String ftpUsername) {
setFtpUsernameValue(ftpUsername);
}
public static void setFtpUsernameValue(String ftpUsername) {
ConfigConstants.ftpUsername = ftpUsername;
}
public static String getFtpPassword() {
return ftpPassword;
}
@Value("${ftp.password:}")
public void setFtpPassword(String ftpPassword) {
setFtpPasswordValue(ftpPassword);
}
public static void setFtpPasswordValue(String ftpPassword) {
ConfigConstants.ftpPassword = ftpPassword;
}
public static String getFtpControlEncoding() {
return ftpControlEncoding;
}
@Value("${ftp.control.encoding:UTF-8}")
public void setFtpControlEncoding(String ftpControlEncoding) {
setFtpControlEncodingValue(ftpControlEncoding);
}
public static void setFtpControlEncodingValue(String ftpControlEncoding) {
ConfigConstants.ftpControlEncoding = ftpControlEncoding;
}
public static String getBaseUrl() {
return baseUrl;
}
@Value("${base.url:default}")
public void setBaseUrl(String baseUrl) {
setBaseUrlValue(baseUrl);
}
public static void setBaseUrlValue(String baseUrl) {
ConfigConstants.baseUrl = baseUrl;
}
public static String getFileDir() {
return fileDir;
}
@Value("${file.dir:default}")
public void setFileDir(String fileDir) {
setFileDirValue(fileDir);
}
public static void setFileDirValue(String fileDir) {
if (!DEFAULT_VALUE.equalsIgnoreCase(fileDir)) {
if (!fileDir.endsWith(File.separator)) {
fileDir = fileDir + File.separator;
}
ConfigConstants.fileDir = fileDir;
}
}
public static String getLocalPreviewDir() {
return localPreviewDir;
}
@Value("${local.preview.dir:default}")
public void setLocalPreviewDir(String localPreviewDir) {
setLocalPreviewDirValue(localPreviewDir);
}
public static void setLocalPreviewDirValue(String localPreviewDir) {
if (!DEFAULT_VALUE.equals(localPreviewDir)) {
if (!localPreviewDir.endsWith(File.separator)) {
localPreviewDir = localPreviewDir + File.separator;
}
}
ConfigConstants.localPreviewDir = localPreviewDir;
}
@Value("${trust.host:default}")
public void setTrustHost(String trustHost) {
setTrustHostSet(getHostValue(trustHost));
}
public static void setTrustHostValue(String trustHost){
setTrustHostSet(getHostValue(trustHost));
}
@Value("${not.trust.host:default}")
public void setNotTrustHost(String notTrustHost) {
setNotTrustHostSet(getHostValue(notTrustHost));
}
public static void setNotTrustHostValue(String notTrustHost){
setNotTrustHostSet(getHostValue(notTrustHost));
}
private static CopyOnWriteArraySet<String> getHostValue(String trustHost) {
if (DEFAULT_VALUE.equalsIgnoreCase(trustHost)) {
return new CopyOnWriteArraySet<>();
} else {
String[] trustHostArray = trustHost.toLowerCase().split(",");
return new CopyOnWriteArraySet<>(Arrays.asList(trustHostArray));
}
}
public static Set<String> getTrustHostSet() {
return trustHostSet;
}
private static void setTrustHostSet(CopyOnWriteArraySet<String> trustHostSet) {
ConfigConstants.trustHostSet = trustHostSet;
}
public static Set<String> getNotTrustHostSet() {
return notTrustHostSet;
}
public static void setNotTrustHostSet(CopyOnWriteArraySet<String> notTrustHostSet) {
ConfigConstants.notTrustHostSet = notTrustHostSet;
}
public static String getPdfPresentationModeDisable() {
return pdfPresentationModeDisable;
}
@Value("${pdf.presentationMode.disable:true}")
public void setPdfPresentationModeDisable(String pdfPresentationModeDisable) {
setPdfPresentationModeDisableValue(pdfPresentationModeDisable);
}
public static void setPdfPresentationModeDisableValue(String pdfPresentationModeDisable) {
ConfigConstants.pdfPresentationModeDisable = pdfPresentationModeDisable;
}
public static String getPdfOpenFileDisable() {
return pdfOpenFileDisable;
}
@Value("${pdf.openFile.disable:true}")
public void setPdfOpenFileDisable(String pdfOpenFileDisable) {
setPdfOpenFileDisableValue(pdfOpenFileDisable);
}
public static void setPdfOpenFileDisableValue(String pdfOpenFileDisable) {
ConfigConstants.pdfOpenFileDisable = pdfOpenFileDisable;
}
public static String getPdfPrintDisable() {
return pdfPrintDisable;
}
@Value("${pdf.print.disable:true}")
public void setPdfPrintDisable(String pdfPrintDisable) {
setPdfPrintDisableValue(pdfPrintDisable);
}
public static void setPdfPrintDisableValue(String pdfPrintDisable) {
ConfigConstants.pdfPrintDisable = pdfPrintDisable;
}
public static String getPdfDownloadDisable() {
return pdfDownloadDisable;
}
@Value("${pdf.download.disable:true}")
public void setPdfDownloadDisable(String pdfDownloadDisable) {
setPdfDownloadDisableValue(pdfDownloadDisable);
}
public static void setPdfDownloadDisableValue(String pdfDownloadDisable) {
ConfigConstants.pdfDownloadDisable = pdfDownloadDisable;
}
public static String getPdfBookmarkDisable() {
return pdfBookmarkDisable;
}
@Value("${pdf.bookmark.disable:true}")
public void setPdfBookmarkDisable(String pdfBookmarkDisable) {
setPdfBookmarkDisableValue(pdfBookmarkDisable);
}
public static void setPdfBookmarkDisableValue(String pdfBookmarkDisable) {
ConfigConstants.pdfBookmarkDisable = pdfBookmarkDisable;
}
public static String getPdfDisableEditing() {
return pdfDisableEditing;
}
@Value("${pdf.disable.editing:true}")
public void setpdfDisableEditing(String pdfDisableEditing) {
setPdfDisableEditingValue(pdfDisableEditing);
}
public static void setPdfDisableEditingValue(String pdfDisableEditing) {
ConfigConstants.pdfDisableEditing = pdfDisableEditing;
}
public static String getOfficePreviewSwitchDisabled() {
return officePreviewSwitchDisabled;
}
@Value("${office.preview.switch.disabled:true}")
public void setOfficePreviewSwitchDisabled(String officePreviewSwitchDisabled) {
ConfigConstants.officePreviewSwitchDisabled = officePreviewSwitchDisabled;
}
public static void setOfficePreviewSwitchDisabledValue(String officePreviewSwitchDisabled) {
ConfigConstants.officePreviewSwitchDisabled = officePreviewSwitchDisabled;
}
public static Boolean getFileUploadDisable() {
return fileUploadDisable;
}
@Value("${file.upload.disable:false}")
public void setFileUploadDisable(Boolean fileUploadDisable) {
setFileUploadDisableValue(fileUploadDisable);
}
public static void setFileUploadDisableValue(Boolean fileUploadDisable) {
ConfigConstants.fileUploadDisable = fileUploadDisable;
}
public static String getTifPreviewType() {
return tifPreviewType;
}
@Value("${tif.preview.type:tif}")
public void setTifPreviewType(String tifPreviewType) {
setTifPreviewTypeValue(tifPreviewType);
}
public static void setTifPreviewTypeValue(String tifPreviewType) {
ConfigConstants.tifPreviewType = tifPreviewType;
}
public static String[] getProhibit() {
return prohibit;
}
@Value("${prohibit:exe,dll}")
public void setProhibit(String prohibit) {
String[] prohibitArr = prohibit.split(",");
setProhibitValue(prohibitArr);
}
public static void setProhibitValue(String[] prohibit) {
ConfigConstants.prohibit = prohibit;
}
public static String maxSize() {
return size;
}
@Value("${spring.servlet.multipart.max-file-size:500MB}")
public void setSize(String size) {
setSizeValue(size);
}
public static void setSizeValue(String size) {
ConfigConstants.size = size;
}
public static String getPassword() {
return password;
}
@Value("${delete.password:123456}")
public void setPassword(String password) {
setPasswordValue(password);
}
public static void setPasswordValue(String password) {
ConfigConstants.password = password;
}
public static int getPdf2JpgDpi() {
return pdf2JpgDpi;
}
@Value("${pdf2jpg.dpi:105}")
public void pdf2JpgDpi(int pdf2JpgDpi) {
setPdf2JpgDpiValue(pdf2JpgDpi);
}
public static void setPdf2JpgDpiValue(int pdf2JpgDpi) {
ConfigConstants.pdf2JpgDpi = pdf2JpgDpi;
}
public static String getOfficeTypeWeb() {
return officeTypeWeb;
}
@Value("${office.type.web:web}")
public void setOfficeTypeWeb(String officeTypeWeb) {
setOfficeTypeWebValue(officeTypeWeb);
}
public static void setOfficeTypeWebValue(String officeTypeWeb) {
ConfigConstants.officeTypeWeb = officeTypeWeb;
}
public static Boolean getDeleteSourceFile() {
return deleteSourceFile;
}
@Value("${delete.source.file:true}")
public void setDeleteSourceFile(Boolean deleteSourceFile) {
setDeleteSourceFileValue(deleteSourceFile);
}
public static void setDeleteSourceFileValue(Boolean deleteSourceFile) {
ConfigConstants.deleteSourceFile = deleteSourceFile;
}
public static Boolean getDeleteCaptcha() {
return deleteCaptcha;
}
@Value("${delete.captcha:false}")
public void setDeleteCaptcha(Boolean deleteCaptcha) {
setDeleteCaptchaValue(deleteCaptcha);
}
public static void setDeleteCaptchaValue(Boolean deleteCaptcha) {
ConfigConstants.deleteCaptcha = deleteCaptcha;
}
/**
* 以下为cad转换模块设置
*/
public static String getCadPreviewType() {
return cadPreviewType;
}
@Value("${cad.preview.type:svg}")
public void setCadPreviewType(String cadPreviewType) {
setCadPreviewTypeValue(cadPreviewType);
}
public static void setCadPreviewTypeValue(String cadPreviewType) {
ConfigConstants.cadPreviewType = cadPreviewType;
}
public static String getCadTimeout() {
return cadTimeout;
}
@Value("${cad.timeout:90}")
public void setCadTimeout(String cadTimeout) {
setCadTimeoutValue(cadTimeout);
}
public static void setCadTimeoutValue(String cadTimeout) {
ConfigConstants.cadTimeout = cadTimeout;
}
public static int getCadThread() {
return cadThread;
}
@Value("${cad.thread:5}")
public void setCadThread(int cadThread) {
setCadThreadValue(cadThread);
}
public static void setCadThreadValue(int cadThread) {
ConfigConstants.cadThread = cadThread;
}
/**
* 以下为OFFICE转换模块设置
*/
public static String getOfficePageRange() {
return officePageRange;
}
@Value("${office.pagerange:false}")
public void setOfficePageRange(String officePageRange) {
setOfficePageRangeValue(officePageRange);
}
public static void setOfficePageRangeValue(String officePageRange) {
ConfigConstants.officePageRange = officePageRange;
}
public static String getOfficeWatermark() {
return officeWatermark;
}
@Value("${office.watermark:false}")
public void setOfficeWatermark(String officeWatermark) {
setOfficeWatermarkValue(officeWatermark);
}
public static void setOfficeWatermarkValue(String officeWatermark) {
ConfigConstants.officeWatermark = officeWatermark;
}
public static String getOfficeQuality() {
return officeQuality;
}
@Value("${office.quality:80}")
public void setOfficeQuality(String officeQuality) {
setOfficeQualityValue(officeQuality);
}
public static void setOfficeQualityValue(String officeQuality) {
ConfigConstants.officeQuality = officeQuality;
}
public static String getOfficeMaxImageResolution() {
return officeMaxImageResolution;
}
@Value("${office.maximageresolution:150}")
public void setOfficeMaxImageResolution(String officeMaxImageResolution) {
setOfficeMaxImageResolutionValue(officeMaxImageResolution);
}
public static void setOfficeMaxImageResolutionValue(String officeMaxImageResolution) {
ConfigConstants.officeMaxImageResolution = officeMaxImageResolution;
}
public static Boolean getOfficeExportBookmarks() {
return officeExportBookmarks;
}
@Value("${office.exportbookmarks:true}")
public void setOfficeExportBookmarks(Boolean officeExportBookmarks) {
setOfficeExportBookmarksValue(officeExportBookmarks);
}
public static void setOfficeExportBookmarksValue(Boolean officeExportBookmarks) {
ConfigConstants.officeExportBookmarks = officeExportBookmarks;
}
public static Boolean getOfficeExportNotes() {
return officeExportNotes;
}
@Value("${office.exportnotes:true}")
public void setExportNotes(Boolean officeExportNotes) {
setOfficeExportNotesValue(officeExportNotes);
}
public static void setOfficeExportNotesValue(Boolean officeExportNotes) {
ConfigConstants.officeExportNotes = officeExportNotes;
}
public static Boolean getOfficeDocumentOpenPasswords() {
return officeDocumentOpenPasswords;
}
@Value("${office.documentopenpasswords:true}")
public void setDocumentOpenPasswords(Boolean officeDocumentOpenPasswords) {
setOfficeDocumentOpenPasswordsValue(officeDocumentOpenPasswords);
}
public static void setOfficeDocumentOpenPasswordsValue(Boolean officeDocumentOpenPasswords) {
ConfigConstants.officeDocumentOpenPasswords = officeDocumentOpenPasswords;
}
/**
* 以下为首页显示
*/
public static String getBeian() {
return beian;
}
@Value("${beian:default}")
public void setBeian(String beian) {
setBeianValue(beian);
}
public static void setBeianValue(String beian) {
ConfigConstants.beian = beian;
}
public static String getHomePageNumber() {
return homePageNumber;
}
@Value("${home.pagenumber:1}")
public void setHomePageNumber(String homePageNumber) {
setHomePageNumberValue(homePageNumber);
}
public static void setHomePageNumberValue(String homePageNumber) {
ConfigConstants.homePageNumber = homePageNumber;
}
public static String getHomePagination() {
return homePagination;
}
@Value("${home.pagination:true}")
public void setHomePagination(String homePagination) {
setHomePaginationValue(homePagination);
}
public static void setHomePaginationValue(String homePagination) {
ConfigConstants.homePagination = homePagination;
}
public static String getHomePageSize() {
return homePageSize;
}
@Value("${home.pagesize:15}")
public void setHomePageSize(String homePageSize) {
setHomePageSizeValue(homePageSize);
}
public static void setHomePageSizeValue(String homePageSize) {
ConfigConstants.homePageSize = homePageSize;
}
public static String getHomeSearch() {
return homeSearch;
}
@Value("${home.search:1}")
public void setHomeSearch(String homeSearch) {
setHomeSearchValue(homeSearch);
}
public static void setHomeSearchValue(String homeSearch) {
ConfigConstants.homeSearch = homeSearch;
}
}

View File

@@ -0,0 +1,206 @@
package cn.keking.config;
import cn.keking.utils.ConfigUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
/**
* @auther: chenjh
* @time: 2019/4/10 16:16
* @description 每隔1s读取并更新一次配置文件
*/
@Component
public class ConfigRefreshComponent {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigRefreshComponent.class);
@PostConstruct
void refresh() {
Thread configRefreshThread = new Thread(new ConfigRefreshThread());
configRefreshThread.start();
}
static class ConfigRefreshThread implements Runnable {
@Override
public void run() {
try {
Properties properties = new Properties();
String text;
String media;
boolean cacheEnabled;
String[] textArray;
String[] mediaArray;
String officePreviewType;
String officePreviewSwitchDisabled;
String ftpUsername;
String ftpPassword;
String ftpControlEncoding;
String configFilePath = ConfigUtils.getCustomizedConfigPath();
String baseUrl;
String trustHost;
String notTrustHost;
String pdfPresentationModeDisable;
String pdfOpenFileDisable;
String pdfPrintDisable;
String pdfDownloadDisable;
String pdfBookmarkDisable;
String pdfDisableEditing;
boolean fileUploadDisable;
String tifPreviewType;
String prohibit;
String[] prohibitArray;
String beian;
String size;
String password;
int pdf2JpgDpi;
String officeTypeWeb;
String cadPreviewType;
boolean deleteSourceFile;
boolean deleteCaptcha;
String officPageRange;
String officWatermark;
String officQuality;
String officMaxImageResolution;
boolean officExportBookmarks;
boolean officeExportNotes;
boolean officeDocumentOpenPasswords;
String cadTimeout;
int cadThread;
String homePageNumber;
String homePagination;
String homePageSize;
String homeSearch;
while (true) {
FileReader fileReader = new FileReader(configFilePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
properties.load(bufferedReader);
ConfigUtils.restorePropertiesFromEnvFormat(properties);
cacheEnabled = Boolean.parseBoolean(properties.getProperty("cache.enabled", ConfigConstants.DEFAULT_CACHE_ENABLED));
text = properties.getProperty("simText", ConfigConstants.DEFAULT_TXT_TYPE);
media = properties.getProperty("media", ConfigConstants.DEFAULT_MEDIA_TYPE);
officePreviewType = properties.getProperty("office.preview.type", ConfigConstants.DEFAULT_OFFICE_PREVIEW_TYPE);
officePreviewSwitchDisabled = properties.getProperty("office.preview.switch.disabled", ConfigConstants.DEFAULT_OFFICE_PREVIEW_SWITCH_DISABLED);
ftpUsername = properties.getProperty("ftp.username", ConfigConstants.DEFAULT_FTP_USERNAME);
ftpPassword = properties.getProperty("ftp.password", ConfigConstants.DEFAULT_FTP_PASSWORD);
ftpControlEncoding = properties.getProperty("ftp.control.encoding", ConfigConstants.DEFAULT_FTP_CONTROL_ENCODING);
textArray = text.split(",");
mediaArray = media.split(",");
baseUrl = properties.getProperty("base.url", ConfigConstants.DEFAULT_VALUE);
trustHost = properties.getProperty("trust.host", ConfigConstants.DEFAULT_VALUE);
notTrustHost = properties.getProperty("not.trust.host", ConfigConstants.DEFAULT_VALUE);
pdfPresentationModeDisable = properties.getProperty("pdf.presentationMode.disable", ConfigConstants.DEFAULT_PDF_PRESENTATION_MODE_DISABLE);
pdfOpenFileDisable = properties.getProperty("pdf.openFile.disable", ConfigConstants.DEFAULT_PDF_OPEN_FILE_DISABLE);
pdfPrintDisable = properties.getProperty("pdf.print.disable", ConfigConstants.DEFAULT_PDF_PRINT_DISABLE);
pdfDownloadDisable = properties.getProperty("pdf.download.disable", ConfigConstants.DEFAULT_PDF_DOWNLOAD_DISABLE);
pdfBookmarkDisable = properties.getProperty("pdf.bookmark.disable", ConfigConstants.DEFAULT_PDF_BOOKMARK_DISABLE);
pdfDisableEditing = properties.getProperty("pdf.disable.editing", ConfigConstants.DEFAULT_PDF_DISABLE_EDITING);
fileUploadDisable = Boolean.parseBoolean(properties.getProperty("file.upload.disable", ConfigConstants.DEFAULT_FILE_UPLOAD_DISABLE));
tifPreviewType = properties.getProperty("tif.preview.type", ConfigConstants.DEFAULT_TIF_PREVIEW_TYPE);
cadPreviewType = properties.getProperty("cad.preview.type", ConfigConstants.DEFAULT_CAD_PREVIEW_TYPE);
size = properties.getProperty("spring.servlet.multipart.max-file-size", ConfigConstants.DEFAULT_SIZE);
beian = properties.getProperty("beian", ConfigConstants.DEFAULT_BEIAN);
prohibit = properties.getProperty("prohibit", ConfigConstants.DEFAULT_PROHIBIT);
password = properties.getProperty("delete.password", ConfigConstants.DEFAULT_PASSWORD);
pdf2JpgDpi = Integer.parseInt(properties.getProperty("pdf2jpg.dpi", ConfigConstants.DEFAULT_PDF2_JPG_DPI));
officeTypeWeb = properties.getProperty("office.type.web", ConfigConstants.DEFAULT_OFFICE_TYPE_WEB);
deleteSourceFile = Boolean.parseBoolean(properties.getProperty("delete.source.file", ConfigConstants.DEFAULT_DELETE_SOURCE_FILE));
deleteCaptcha = Boolean.parseBoolean(properties.getProperty("delete.captcha", ConfigConstants.DEFAULT_DELETE_CAPTCHA));
officPageRange = properties.getProperty("office.pagerange", ConfigConstants.DEFAULT_OFFICE_PAQERANQE);
officWatermark = properties.getProperty("office.watermark", ConfigConstants.DEFAULT_OFFICE_WATERMARK);
officQuality = properties.getProperty("office.quality", ConfigConstants.DEFAULT_OFFICE_QUALITY);
officMaxImageResolution = properties.getProperty("office.maximageresolution", ConfigConstants.DEFAULT_OFFICE_MAXIMAQERESOLUTION);
officExportBookmarks = Boolean.parseBoolean(properties.getProperty("office.exportbookmarks", ConfigConstants.DEFAULT_OFFICE_EXPORTBOOKMARKS));
officeExportNotes = Boolean.parseBoolean(properties.getProperty("office.exportnotes", ConfigConstants.DEFAULT_OFFICE_EXPORTNOTES));
officeDocumentOpenPasswords = Boolean.parseBoolean(properties.getProperty("office.documentopenpasswords", ConfigConstants.DEFAULT_OFFICE_EOCUMENTOPENPASSWORDS));
cadTimeout = properties.getProperty("cad.timeout", ConfigConstants.DEFAULT_CAD_TIMEOUT);
homePageNumber = properties.getProperty("home.pagenumber", ConfigConstants.DEFAULT_HOME_PAGENUMBER);
homePagination = properties.getProperty("home.pagination", ConfigConstants.DEFAULT_HOME_PAGINATION);
homePageSize = properties.getProperty("home.pagesize", ConfigConstants.DEFAULT_HOME_PAGSIZE);
homeSearch = properties.getProperty("home.search", ConfigConstants.DEFAULT_HOME_SEARCH);
cadThread = Integer.parseInt(properties.getProperty("cad.thread", ConfigConstants.DEFAULT_CAD_THREAD));
prohibitArray = prohibit.split(",");
ConfigConstants.setCacheEnabledValueValue(cacheEnabled);
ConfigConstants.setSimTextValue(textArray);
ConfigConstants.setMediaValue(mediaArray);
ConfigConstants.setOfficePreviewTypeValue(officePreviewType);
ConfigConstants.setFtpUsernameValue(ftpUsername);
ConfigConstants.setFtpPasswordValue(ftpPassword);
ConfigConstants.setFtpControlEncodingValue(ftpControlEncoding);
ConfigConstants.setBaseUrlValue(baseUrl);
ConfigConstants.setTrustHostValue(trustHost);
ConfigConstants.setNotTrustHostValue(notTrustHost);
ConfigConstants.setOfficePreviewSwitchDisabledValue(officePreviewSwitchDisabled);
ConfigConstants.setPdfPresentationModeDisableValue(pdfPresentationModeDisable);
ConfigConstants.setPdfOpenFileDisableValue(pdfOpenFileDisable);
ConfigConstants.setPdfPrintDisableValue(pdfPrintDisable);
ConfigConstants.setPdfDownloadDisableValue(pdfDownloadDisable);
ConfigConstants.setPdfBookmarkDisableValue(pdfBookmarkDisable);
ConfigConstants.setPdfDisableEditingValue(pdfDisableEditing);
ConfigConstants.setFileUploadDisableValue(fileUploadDisable);
ConfigConstants.setTifPreviewTypeValue(tifPreviewType);
ConfigConstants.setCadPreviewTypeValue(cadPreviewType);
ConfigConstants.setBeianValue(beian);
ConfigConstants.setSizeValue(size);
ConfigConstants.setProhibitValue(prohibitArray);
ConfigConstants.setPasswordValue(password);
ConfigConstants.setPdf2JpgDpiValue(pdf2JpgDpi);
ConfigConstants.setOfficeTypeWebValue(officeTypeWeb);
ConfigConstants.setOfficePageRangeValue(officPageRange);
ConfigConstants.setOfficeWatermarkValue(officWatermark);
ConfigConstants.setOfficeQualityValue(officQuality);
ConfigConstants.setOfficeMaxImageResolutionValue(officMaxImageResolution);
ConfigConstants.setOfficeExportBookmarksValue(officExportBookmarks);
ConfigConstants.setOfficeExportNotesValue(officeExportNotes);
ConfigConstants.setOfficeDocumentOpenPasswordsValue(officeDocumentOpenPasswords);
ConfigConstants.setDeleteSourceFileValue(deleteSourceFile);
ConfigConstants.setDeleteCaptchaValue(deleteCaptcha);
ConfigConstants.setCadTimeoutValue(cadTimeout);
ConfigConstants.setCadThreadValue(cadThread);
ConfigConstants.setHomePageNumberValue(homePageNumber);
ConfigConstants.setHomePaginationValue(homePagination);
ConfigConstants.setHomePageSizeValue(homePageSize);
ConfigConstants.setHomeSearchValue(homeSearch);
setWatermarkConfig(properties);
bufferedReader.close();
fileReader.close();
TimeUnit.SECONDS.sleep(1);
}
} catch (IOException | InterruptedException e) {
LOGGER.error("读取配置文件异常", e);
}
}
private void setWatermarkConfig(Properties properties) {
String watermarkTxt = properties.getProperty("watermark.txt", WatermarkConfigConstants.DEFAULT_WATERMARK_TXT);
String watermarkXSpace = properties.getProperty("watermark.x.space", WatermarkConfigConstants.DEFAULT_WATERMARK_X_SPACE);
String watermarkYSpace = properties.getProperty("watermark.y.space", WatermarkConfigConstants.DEFAULT_WATERMARK_Y_SPACE);
String watermarkFont = properties.getProperty("watermark.font", WatermarkConfigConstants.DEFAULT_WATERMARK_FONT);
String watermarkFontsize = properties.getProperty("watermark.fontsize", WatermarkConfigConstants.DEFAULT_WATERMARK_FONTSIZE);
String watermarkColor = properties.getProperty("watermark.color", WatermarkConfigConstants.DEFAULT_WATERMARK_COLOR);
String watermarkAlpha = properties.getProperty("watermark.alpha", WatermarkConfigConstants.DEFAULT_WATERMARK_ALPHA);
String watermarkWidth = properties.getProperty("watermark.width", WatermarkConfigConstants.DEFAULT_WATERMARK_WIDTH);
String watermarkHeight = properties.getProperty("watermark.height", WatermarkConfigConstants.DEFAULT_WATERMARK_HEIGHT);
String watermarkAngle = properties.getProperty("watermark.angle", WatermarkConfigConstants.DEFAULT_WATERMARK_ANGLE);
WatermarkConfigConstants.setWatermarkTxtValue(watermarkTxt);
WatermarkConfigConstants.setWatermarkXSpaceValue(watermarkXSpace);
WatermarkConfigConstants.setWatermarkYSpaceValue(watermarkYSpace);
WatermarkConfigConstants.setWatermarkFontValue(watermarkFont);
WatermarkConfigConstants.setWatermarkFontsizeValue(watermarkFontsize);
WatermarkConfigConstants.setWatermarkColorValue(watermarkColor);
WatermarkConfigConstants.setWatermarkAlphaValue(watermarkAlpha);
WatermarkConfigConstants.setWatermarkWidthValue(watermarkWidth);
WatermarkConfigConstants.setWatermarkHeightValue(watermarkHeight);
WatermarkConfigConstants.setWatermarkAngleValue(watermarkAngle);
}
}
}

View File

@@ -0,0 +1,243 @@
package cn.keking.config;
import io.netty.channel.nio.NioEventLoopGroup;
import org.apache.commons.lang3.StringUtils;
import org.redisson.client.codec.Codec;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ClassUtils;
/**
* Created by kl on 2017/09/26.
* redisson 客户端配置
*/
@ConditionalOnExpression("'${cache.type:default}'.equals('redis')")
@ConfigurationProperties(prefix = "spring.redisson")
@Configuration
public class RedissonConfig {
private String address;
private int connectionMinimumIdleSize = 10;
private int idleConnectionTimeout=10000;
private int pingTimeout=1000;
private int connectTimeout=10000;
private int timeout=3000;
private int retryAttempts=3;
private int retryInterval=1500;
private int reconnectionTimeout=3000;
private int failedAttempts=3;
private String password = null;
private int subscriptionsPerConnection=5;
private String clientName=null;
private int subscriptionConnectionMinimumIdleSize = 1;
private int subscriptionConnectionPoolSize = 50;
private int connectionPoolSize = 64;
private int database = 0;
private boolean dnsMonitoring = false;
private int dnsMonitoringInterval = 5000;
private int thread; //当前处理核数量 * 2
private String codec="org.redisson.codec.JsonJacksonCodec";
@Bean
Config config() throws Exception {
Config config = new Config();
config.useSingleServer().setAddress(address)
.setConnectionMinimumIdleSize(connectionMinimumIdleSize)
.setConnectionPoolSize(connectionPoolSize)
.setDatabase(database)
.setDnsMonitoring(dnsMonitoring)
.setDnsMonitoringInterval(dnsMonitoringInterval)
.setSubscriptionConnectionMinimumIdleSize(subscriptionConnectionMinimumIdleSize)
.setSubscriptionConnectionPoolSize(subscriptionConnectionPoolSize)
.setSubscriptionsPerConnection(subscriptionsPerConnection)
.setClientName(clientName)
.setFailedAttempts(failedAttempts)
.setRetryAttempts(retryAttempts)
.setRetryInterval(retryInterval)
.setReconnectionTimeout(reconnectionTimeout)
.setTimeout(timeout)
.setConnectTimeout(connectTimeout)
.setIdleConnectionTimeout(idleConnectionTimeout)
.setPingTimeout(pingTimeout)
.setPassword(StringUtils.trimToNull(password));
Codec codec=(Codec) ClassUtils.forName(getCodec(), ClassUtils.getDefaultClassLoader()).newInstance();
config.setCodec(codec);
config.setThreads(thread);
config.setEventLoopGroup(new NioEventLoopGroup());
config.setUseLinuxNativeEpoll(false);
return config;
}
public int getThread() {
return thread;
}
public void setThread(int thread) {
this.thread = thread;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getIdleConnectionTimeout() {
return idleConnectionTimeout;
}
public void setIdleConnectionTimeout(int idleConnectionTimeout) {
this.idleConnectionTimeout = idleConnectionTimeout;
}
public int getPingTimeout() {
return pingTimeout;
}
public void setPingTimeout(int pingTimeout) {
this.pingTimeout = pingTimeout;
}
public int getConnectTimeout() {
return connectTimeout;
}
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getRetryAttempts() {
return retryAttempts;
}
public void setRetryAttempts(int retryAttempts) {
this.retryAttempts = retryAttempts;
}
public int getRetryInterval() {
return retryInterval;
}
public void setRetryInterval(int retryInterval) {
this.retryInterval = retryInterval;
}
public int getReconnectionTimeout() {
return reconnectionTimeout;
}
public void setReconnectionTimeout(int reconnectionTimeout) {
this.reconnectionTimeout = reconnectionTimeout;
}
public int getFailedAttempts() {
return failedAttempts;
}
public void setFailedAttempts(int failedAttempts) {
this.failedAttempts = failedAttempts;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getSubscriptionsPerConnection() {
return subscriptionsPerConnection;
}
public void setSubscriptionsPerConnection(int subscriptionsPerConnection) {
this.subscriptionsPerConnection = subscriptionsPerConnection;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public int getSubscriptionConnectionMinimumIdleSize() {
return subscriptionConnectionMinimumIdleSize;
}
public void setSubscriptionConnectionMinimumIdleSize(int subscriptionConnectionMinimumIdleSize) {
this.subscriptionConnectionMinimumIdleSize = subscriptionConnectionMinimumIdleSize;
}
public int getSubscriptionConnectionPoolSize() {
return subscriptionConnectionPoolSize;
}
public void setSubscriptionConnectionPoolSize(int subscriptionConnectionPoolSize) {
this.subscriptionConnectionPoolSize = subscriptionConnectionPoolSize;
}
public int getConnectionMinimumIdleSize() {
return connectionMinimumIdleSize;
}
public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) {
this.connectionMinimumIdleSize = connectionMinimumIdleSize;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public int getDatabase() {
return database;
}
public void setDatabase(int database) {
this.database = database;
}
public boolean isDnsMonitoring() {
return dnsMonitoring;
}
public void setDnsMonitoring(boolean dnsMonitoring) {
this.dnsMonitoring = dnsMonitoring;
}
public int getDnsMonitoringInterval() {
return dnsMonitoringInterval;
}
public void setDnsMonitoringInterval(int dnsMonitoringInterval) {
this.dnsMonitoringInterval = dnsMonitoringInterval;
}
public String getCodec() {
return codec;
}
public void setCodec(String codec) {
this.codec = codec;
}
}

View File

@@ -0,0 +1,37 @@
package cn.keking.config;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.KkFileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
/**
* @auther: chenjh
* @since: 2019/6/11 7:45
*/
@Component
@ConditionalOnExpression("'${cache.clean.enabled:false}'.equals('true')")
public class SchedulerCleanConfig {
private final Logger logger = LoggerFactory.getLogger(SchedulerCleanConfig.class);
private final CacheService cacheService;
public SchedulerCleanConfig(CacheService cacheService) {
this.cacheService = cacheService;
}
private final String fileDir = ConfigConstants.getFileDir();
//默认每晚3点执行一次
@Scheduled(cron = "${cache.clean.cron:0 0 3 * * ?}")
public void clean() {
logger.info("Cache clean start");
cacheService.cleanCache();
KkFileUtils.deleteDirectory(fileDir);
logger.info("Cache clean end");
}
}

View File

@@ -0,0 +1,164 @@
package cn.keking.config;
import org.springframework.beans.factory.annotation.Value;
/**
* @author chenjh
* @since 2020/5/13 17:44
*/
public class WatermarkConfigConstants {
private static String WATERMARK_TXT;
private static String WATERMARK_X_SPACE;
private static String WATERMARK_Y_SPACE;
private static String WATERMARK_FONT;
private static String WATERMARK_FONTSIZE;
private static String WATERMARK_COLOR;
private static String WATERMARK_ALPHA;
private static String WATERMARK_WIDTH;
private static String WATERMARK_HEIGHT;
private static String WATERMARK_ANGLE;
public static String DEFAULT_WATERMARK_TXT = "";
public static String DEFAULT_WATERMARK_X_SPACE = "10";
public static String DEFAULT_WATERMARK_Y_SPACE = "10";
public static String DEFAULT_WATERMARK_FONT = "微软雅黑";
public static String DEFAULT_WATERMARK_FONTSIZE = "18px";
public static String DEFAULT_WATERMARK_COLOR = "black";
public static String DEFAULT_WATERMARK_ALPHA = "0.2";
public static String DEFAULT_WATERMARK_WIDTH = "240";
public static String DEFAULT_WATERMARK_HEIGHT = "80";
public static String DEFAULT_WATERMARK_ANGLE = "10";
public static String getWatermarkTxt() {
return WATERMARK_TXT;
}
public static void setWatermarkTxtValue(String watermarkTxt) {
WATERMARK_TXT = watermarkTxt;
}
@Value("${watermark.txt:}")
public void setWatermarkTxt(String watermarkTxt) {
setWatermarkTxtValue(watermarkTxt);
}
public static String getWatermarkXSpace() {
return WATERMARK_X_SPACE;
}
public static void setWatermarkXSpaceValue(String watermarkXSpace) {
WATERMARK_X_SPACE = watermarkXSpace;
}
@Value("${watermark.x.space:10}")
public void setWatermarkXSpace(String watermarkXSpace) {
setWatermarkXSpaceValue(watermarkXSpace);
}
public static String getWatermarkYSpace() {
return WATERMARK_Y_SPACE;
}
public static void setWatermarkYSpaceValue(String watermarkYSpace) {
WATERMARK_Y_SPACE = watermarkYSpace;
}
@Value("${watermark.y.space:10}")
public void setWatermarkYSpace(String watermarkYSpace) {
setWatermarkYSpaceValue(watermarkYSpace);
}
public static String getWatermarkFont() {
return WATERMARK_FONT;
}
public static void setWatermarkFontValue(String watermarkFont) {
WATERMARK_FONT = watermarkFont;
}
@Value("${watermark.font:微软雅黑}")
public void setWatermarkFont(String watermarkFont) {
setWatermarkFontValue(watermarkFont);
}
public static String getWatermarkFontsize() {
return WATERMARK_FONTSIZE;
}
public static void setWatermarkFontsizeValue(String watermarkFontsize) {
WATERMARK_FONTSIZE = watermarkFontsize;
}
@Value("${watermark.fontsize:18px}")
public void setWatermarkFontsize(String watermarkFontsize) {
setWatermarkFontsizeValue(watermarkFontsize);
}
public static String getWatermarkColor() {
return WATERMARK_COLOR;
}
public static void setWatermarkColorValue(String watermarkColor) {
WATERMARK_COLOR = watermarkColor;
}
@Value("${watermark.color:black}")
public void setWatermarkColor(String watermarkColor) {
setWatermarkColorValue(watermarkColor);
}
public static String getWatermarkAlpha() {
return WATERMARK_ALPHA;
}
public static void setWatermarkAlphaValue(String watermarkAlpha) {
WATERMARK_ALPHA = watermarkAlpha;
}
@Value("${watermark.alpha:0.2}")
public void setWatermarkAlpha(String watermarkAlpha) {
setWatermarkAlphaValue(watermarkAlpha);
}
public static String getWatermarkWidth() {
return WATERMARK_WIDTH;
}
public static void setWatermarkWidthValue(String watermarkWidth) {
WATERMARK_WIDTH = watermarkWidth;
}
@Value("${watermark.width:240}")
public void setWatermarkWidth(String watermarkWidth) {
WATERMARK_WIDTH = watermarkWidth;
}
public static String getWatermarkHeight() {
return WATERMARK_HEIGHT;
}
public static void setWatermarkHeightValue(String watermarkHeight) {
WATERMARK_HEIGHT = watermarkHeight;
}
@Value("${watermark.height:80}")
public void setWatermarkHeight(String watermarkHeight) {
WATERMARK_HEIGHT = watermarkHeight;
}
public static String getWatermarkAngle() {
return WATERMARK_ANGLE;
}
public static void setWatermarkAngleValue(String watermarkAngle) {
WATERMARK_ANGLE = watermarkAngle;
}
@Value("${watermark.angle:10}")
public void setWatermarkAngle(String watermarkAngle) {
WATERMARK_ANGLE = watermarkAngle;
}
}

View File

@@ -0,0 +1,102 @@
package cn.keking.config;
import cn.keking.web.filter.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.HashSet;
import java.util.Set;
/**
* @author: chenjh
* @since: 2019/4/16 20:04
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final static Logger LOGGER = LoggerFactory.getLogger(WebConfig.class);
/**
* 访问外部文件配置
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String filePath = ConfigConstants.getFileDir();
LOGGER.info("Add resource locations: {}", filePath);
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/","classpath:/resources/","classpath:/static/","classpath:/public/","file:" + filePath);
}
@Bean
public FilterRegistrationBean<ChinesePathFilter> getChinesePathFilter() {
ChinesePathFilter filter = new ChinesePathFilter();
FilterRegistrationBean<ChinesePathFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setOrder(10);
return registrationBean;
}
@Bean
public FilterRegistrationBean<TrustHostFilter> getTrustHostFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
filterUri.add("/getCorsFile");
TrustHostFilter filter = new TrustHostFilter();
FilterRegistrationBean<TrustHostFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean<TrustDirFilter> getTrustDirFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
filterUri.add("/getCorsFile");
TrustDirFilter filter = new TrustDirFilter();
FilterRegistrationBean<TrustDirFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean<BaseUrlFilter> getBaseUrlFilter() {
Set<String> filterUri = new HashSet<>();
BaseUrlFilter filter = new BaseUrlFilter();
FilterRegistrationBean<BaseUrlFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
registrationBean.setOrder(20);
return registrationBean;
}
@Bean
public FilterRegistrationBean<UrlCheckFilter> getUrlCheckFilter() {
UrlCheckFilter filter = new UrlCheckFilter();
FilterRegistrationBean<UrlCheckFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setOrder(30);
return registrationBean;
}
@Bean
public FilterRegistrationBean<AttributeSetFilter> getWatermarkConfigFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/index");
filterUri.add("/");
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
AttributeSetFilter filter = new AttributeSetFilter();
FilterRegistrationBean<AttributeSetFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
}

View File

@@ -0,0 +1,185 @@
package cn.keking.model;
import cn.keking.config.ConfigConstants;
/**
* Created by kl on 2018/1/17.
* Content :
*/
public class FileAttribute {
private FileType type;
private String suffix;
private String name;
private String url;
private boolean isCompressFile = false;
private String compressFileKey;
private String filePassword;
private boolean usePasswordCache;
private String officePreviewType = ConfigConstants.getOfficePreviewType();
private String tifPreviewType;
private Boolean skipDownLoad = false;
private Boolean forceUpdatedCache = false;
private String cacheName;
private String outFilePath;
private String originFilePath;
private String cacheListName;
private boolean isHtmlView = false;
/**
* 代理请求到文件服务器的认证请求头,格式如下:
* {“username”:"test","password":"test"}
* 请求文件服务器时,会将 json 直接塞到请求头里
*/
private String kkProxyAuthorization;
public FileAttribute() {
}
public FileAttribute(FileType type, String suffix, String name, String url) {
this.type = type;
this.suffix = suffix;
this.name = name;
this.url = url;
}
public FileAttribute(FileType type, String suffix, String name, String url, String officePreviewType) {
this.type = type;
this.suffix = suffix;
this.name = name;
this.url = url;
this.officePreviewType = officePreviewType;
}
public boolean isCompressFile() {
return isCompressFile;
}
public void setCompressFile(boolean compressFile) {
isCompressFile = compressFile;
}
public String getFilePassword() {
return filePassword;
}
public void setFilePassword(String filePassword) {
this.filePassword = filePassword;
}
public boolean getUsePasswordCache() {
return usePasswordCache;
}
public void setUsePasswordCache(boolean usePasswordCache) {
this.usePasswordCache = usePasswordCache;
}
public String getOfficePreviewType() {
return officePreviewType;
}
public void setOfficePreviewType(String officePreviewType) {
this.officePreviewType = officePreviewType;
}
public FileType getType() {
return type;
}
public void setType(FileType type) {
this.type = type;
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public String getCompressFileKey() {
return compressFileKey;
}
public void setCompressFileKey(String compressFileKey) {
this.compressFileKey = compressFileKey;
}
public String getName() {
return name;
}
public String getCacheName() {
return cacheName;
}
public String getCacheListName() {
return cacheListName;
}
public String getOutFilePath() {
return outFilePath;
}
public String getOriginFilePath() {
return originFilePath;
}
public boolean isHtmlView() {
return isHtmlView;
}
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
public void setCacheListName(String cacheListName) {
this.cacheListName = cacheListName;
}
public void setOutFilePath(String outFilePath) {
this.outFilePath = outFilePath;
}
public void setOriginFilePath(String originFilePath) {
this.originFilePath = originFilePath;
}
public void setHtmlView(boolean isHtmlView) {
this.isHtmlView = isHtmlView;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Boolean getSkipDownLoad() {
return skipDownLoad;
}
public void setSkipDownLoad(Boolean skipDownLoad) {
this.skipDownLoad = skipDownLoad;
}
public String getTifPreviewType() {
return tifPreviewType;
}
public void setTifPreviewType(String previewType) {
this.tifPreviewType = previewType;
}
public Boolean forceUpdatedCache() {
return forceUpdatedCache;
}
public void setForceUpdatedCache(Boolean forceUpdatedCache) {
this.forceUpdatedCache = forceUpdatedCache;
}
public String getKkProxyAuthorization() {
return kkProxyAuthorization;
}
public void setKkProxyAuthorization(String kkProxyAuthorization) {
this.kkProxyAuthorization = kkProxyAuthorization;
}
}

View File

@@ -0,0 +1,149 @@
package cn.keking.model;
import cn.keking.config.ConfigConstants;
import java.util.HashMap;
import java.util.Map;
/**
* Created by kl on 2018/1/17.
* Content :文件类型文本office压缩包等等
*/
public enum FileType {
PICTURE("pictureFilePreviewImpl"),
COMPRESS("compressFilePreviewImpl"),
OFFICE("officeFilePreviewImpl"),
SIMTEXT("simTextFilePreviewImpl"),
PDF("pdfFilePreviewImpl"),
CODE("codeFilePreviewImpl"),
OTHER("otherFilePreviewImpl"),
MEDIA("mediaFilePreviewImpl"),
MEDIACONVERT("mediaFilePreviewImpl"),
MARKDOWN("markdownFilePreviewImpl"),
XML("xmlFilePreviewImpl"),
CAD("cadFilePreviewImpl"),
TIFF("tiffFilePreviewImpl"),
OFD("ofdFilePreviewImpl"),
EML("emlFilePreviewImpl"),
ONLINE3D("online3DFilePreviewImpl"),
XMIND("xmindFilePreviewImpl"),
SVG("svgFilePreviewImpl"),
EPUB("epubFilePreviewImpl"),
BPMN("bpmnFilePreviewImpl"),
DCM("dcmFilePreviewImpl"),
DRAWIO("drawioFilePreviewImpl");
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","dotm","ett","xlt","xltm","wpt","dot","xlam","dotx","xla","pages", "eps"};
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[] ONLINE3D_TYPES = {"obj", "3ds", "stl", "ply", "off", "3dm", "fbx", "dae", "wrl", "3mf", "ifc","glb","o3dv","gltf","stp","bim","fcstd","step","iges","brep"};
private static final String[] EML_TYPES = {"eml"};
private static final String[] XMIND_TYPES = {"xmind"};
private static final String[] EPUB_TYPES = {"epub"};
private static final String[] DCM_TYPES = {"dcm"};
private static final String[] DRAWIO_TYPES = {"drawio"};
private static final String[] XML_TYPES = {"xml","xbrl"};
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", "dwf", "iges", "igs", "dwt", "dng", "ifc", "dwfx", "stl", "cf2", "plt"};
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", "sql"};
private static final String[] MEDIA_TYPES = ConfigConstants.getMedia();
public static final String[] MEDIA_CONVERT_TYPES = ConfigConstants.getConvertMedias();
private static final Map<String, FileType> FILE_TYPE_MAPPER = new HashMap<>();
static {
for (String office : OFFICE_TYPES) {
FILE_TYPE_MAPPER.put(office, FileType.OFFICE);
}
for (String picture : PICTURE_TYPES) {
FILE_TYPE_MAPPER.put(picture, FileType.PICTURE);
}
for (String archive : ARCHIVE_TYPES) {
FILE_TYPE_MAPPER.put(archive, FileType.COMPRESS);
}
for (String text : SSIM_TEXT_TYPES) {
FILE_TYPE_MAPPER.put(text, FileType.SIMTEXT);
}
for (String media : MEDIA_TYPES) {
FILE_TYPE_MAPPER.put(media, FileType.MEDIA);
}
for (String MEDIACONVERT : MEDIA_CONVERT_TYPES) {
FILE_TYPE_MAPPER.put( MEDIACONVERT, FileType. MEDIACONVERT);
}
for (String tif : TIFF_TYPES) {
FILE_TYPE_MAPPER.put(tif, FileType.TIFF);
}
for (String code : CODES) {
FILE_TYPE_MAPPER.put(code, FileType.CODE);
}
for (String ofd : OFD_TYPES) {
FILE_TYPE_MAPPER.put(ofd, FileType.OFD);
}
for (String cad : CAD_TYPES) {
FILE_TYPE_MAPPER.put(cad, FileType.CAD);
}
for (String svg : SVG_TYPES) {
FILE_TYPE_MAPPER.put(svg, FileType.SVG);
}
for (String epub : EPUB_TYPES) {
FILE_TYPE_MAPPER.put(epub, FileType.EPUB);
}
for (String eml : EML_TYPES) {
FILE_TYPE_MAPPER.put(eml, FileType.EML);
}
for (String xmind : XMIND_TYPES) {
FILE_TYPE_MAPPER.put(xmind, FileType.XMIND);
}
for (String online3D : ONLINE3D_TYPES) {
FILE_TYPE_MAPPER.put(online3D, FileType.ONLINE3D);
}
for (String dcm : DCM_TYPES) {
FILE_TYPE_MAPPER.put(dcm, FileType.DCM);
}
for (String drawio : DRAWIO_TYPES) {
FILE_TYPE_MAPPER.put(drawio, FileType.DRAWIO);
}
for (String xml : XML_TYPES) {
FILE_TYPE_MAPPER.put(xml, FileType.XML);
}
FILE_TYPE_MAPPER.put("md", FileType.MARKDOWN);
FILE_TYPE_MAPPER.put("pdf", FileType.PDF);
FILE_TYPE_MAPPER.put("bpmn", FileType.BPMN);
}
private static FileType to(String fileType) {
return FILE_TYPE_MAPPER.getOrDefault(fileType, OTHER);
}
/**
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
*
* @param url url
* @return 文件类型
*/
public static FileType typeFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return typeFromFileName(fileName);
}
public static FileType typeFromFileName(String fileName) {
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
String lowerCaseFileType = fileType.toLowerCase();
return FileType.to(lowerCaseFileType);
}
private final String instanceName;
FileType(String instanceName) {
this.instanceName = instanceName;
}
public String getInstanceName() {
return instanceName;
}
}

View File

@@ -0,0 +1,88 @@
package cn.keking.model;
import java.io.Serializable;
/**
* 接口返回值结构
*
* @author yudian-it
* @date 2017/11/17
*/
public class ReturnResponse<T> implements Serializable {
private static final long serialVersionUID = 313975329998789878L;
public static final int SUCCESS_CODE = 0;
public static final int FAILURE_CODE = 1;
public static final String SUCCESS_MSG = "SUCCESS";
public static final String FAILURE_MSG = "FAILURE";
/**
* 返回状态
* 0. 成功
* 1. 失败
*/
private int code;
/**
* 返回状态描述
* XXX成功
* XXX失败
*/
private String msg;
private T content;
public ReturnResponse(int code, String msg, T content) {
this.code = code;
this.msg = msg;
this.content = content;
}
public static ReturnResponse<Object> failure(String errMsg) {
return new ReturnResponse<>(FAILURE_CODE, errMsg, null);
}
public static ReturnResponse<Object> failure() {
return failure(FAILURE_MSG);
}
public static ReturnResponse<Object> success(){
return success(null);
}
public static ReturnResponse<Object> success(Object content) {
return new ReturnResponse<>(SUCCESS_CODE, SUCCESS_MSG, content);
}
public boolean isSuccess(){
return SUCCESS_CODE == code;
}
public boolean isFailure(){
return !isSuccess();
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getContent() {
return content;
}
public void setContent(T content) {
this.content = content;
}
}

View File

@@ -0,0 +1,109 @@
package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.utils.RarUtils;
import cn.keking.web.filter.BaseUrlFilter;
import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.IInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* @author yudian-it
* create 2017/11/27
*/
@Component
public class CompressFileReader {
private final FileHandlerService fileHandlerService;
private static final String fileDir = ConfigConstants.getFileDir();
public CompressFileReader(FileHandlerService fileHandlerService) {
this.fileHandlerService = fileHandlerService;
}
public String unRar(String filePath, String filePassword, String fileName, FileAttribute fileAttribute) throws Exception {
List<String> imgUrls = new ArrayList<>();
String baseUrl = BaseUrlFilter.getBaseUrl();
String packagePath = "_"; //防止文件名重复 压缩包统一生成文件添加_符号
String folderName = filePath.replace(fileDir, ""); //修复压缩包 多重目录获取路径错误
if (fileAttribute.isCompressFile()) { //压缩包文件 直接赋予路径 不予下载
folderName = "_decompression" + folderName; //重新修改多重压缩包 生成文件路径
}
RandomAccessFile randomAccessFile = null;
IInArchive inArchive = null;
try {
randomAccessFile = new RandomAccessFile(filePath, "r");
inArchive = SevenZip.openInArchive(null, new RandomAccessFileInStream(randomAccessFile));
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
final String[] str = {null};
for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
if (!item.isFolder()) {
ExtractOperationResult result;
String finalFolderName = folderName;
result = item.extractSlow(data -> {
try {
str[0] = RarUtils.getUtf8String(item.getPath());
if (RarUtils.isMessyCode(str[0])) {
str[0] = new String(item.getPath().getBytes(StandardCharsets.ISO_8859_1), "gbk");
}
str[0] = str[0].replace("\\", File.separator); //Linux 下路径错误
String str1 = str[0].substring(0, str[0].lastIndexOf(File.separator) + 1);
File file = new File(fileDir, finalFolderName + packagePath + File.separator + str1);
if (!file.exists()) {
file.mkdirs();
}
OutputStream out = new FileOutputStream(fileDir + finalFolderName + packagePath + File.separator + str[0], true);
IOUtils.write(data, out);
out.close();
} catch (Exception e) {
e.printStackTrace();
return Integer.parseInt(null);
}
return data.length;
}, filePassword);
if (result == ExtractOperationResult.OK) {
FileType type = FileType.typeFromUrl(str[0]);
if (type.equals(FileType.PICTURE)) {
imgUrls.add(baseUrl + folderName + packagePath + "/" + str[0].replace("\\", "/"));
}
fileHandlerService.putImgCache(fileName + packagePath, imgUrls);
} else {
return null;
}
}
}
return folderName + packagePath;
} catch (Exception e) {
throw new Exception(e);
} finally {
if (inArchive != null) {
try {
inArchive.close();
} catch (SevenZipException e) {
System.err.println("Error closing archive: " + e);
}
}
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (IOException e) {
System.err.println("Error closing file: " + e);
}
}
}
}
}

View File

@@ -0,0 +1,89 @@
package cn.keking.service;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.service.cache.CacheService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.ui.ExtendedModelMap;
import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;
/**
* Created by kl on 2018/1/19.
* Content :消费队列中的转换文件
*/
@Service
public class FileConvertQueueTask {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FileHandlerService fileHandlerService;
public FileConvertQueueTask(FilePreviewFactory previewFactory, CacheService cacheService, FileHandlerService fileHandlerService) {
this.previewFactory = previewFactory;
this.cacheService = cacheService;
this.fileHandlerService = fileHandlerService;
}
@PostConstruct
public void startTask() {
new Thread(new ConvertTask(previewFactory, cacheService, fileHandlerService))
.start();
logger.info("队列处理文件转换任务启动完成 ");
}
static class ConvertTask implements Runnable {
private final Logger logger = LoggerFactory.getLogger(ConvertTask.class);
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FileHandlerService fileHandlerService;
public ConvertTask(FilePreviewFactory previewFactory,
CacheService cacheService,
FileHandlerService fileHandlerService) {
this.previewFactory = previewFactory;
this.cacheService = cacheService;
this.fileHandlerService = fileHandlerService;
}
@Override
public void run() {
while (true) {
String url = null;
try {
url = cacheService.takeQueueTask();
if (url != null) {
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(url, null);
FileType fileType = fileAttribute.getType();
logger.info("正在处理预览转换任务url{},预览类型:{}", url, fileType);
if (isNeedConvert(fileType)) {
FilePreview filePreview = previewFactory.get(fileAttribute);
filePreview.filePreviewHandle(url, new ExtendedModelMap(), fileAttribute);
} else {
logger.info("预览类型无需处理url{},预览类型:{}", url, fileType);
}
}
} catch (Exception e) {
try {
TimeUnit.SECONDS.sleep(10);
} catch (Exception ex) {
Thread.currentThread().interrupt();
ex.printStackTrace();
}
logger.info("处理预览转换任务异常url{}", url, e);
}
}
}
public boolean isNeedConvert(FileType fileType) {
return fileType.equals(FileType.COMPRESS) || fileType.equals(FileType.OFFICE) || fileType.equals(FileType.CAD);
}
}
}

View File

@@ -0,0 +1,589 @@
package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.service.cache.CacheService;
import cn.keking.service.cache.NotResourceCache;
import cn.keking.utils.EncodingDetects;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.UrlEncoderUtils;
import cn.keking.utils.WebUtils;
import cn.keking.web.filter.BaseUrlFilter;
import com.aspose.cad.*;
import com.aspose.cad.fileformats.cad.CadDrawTypeMode;
import com.aspose.cad.fileformats.tiff.enums.TiffExpectedFormat;
import com.aspose.cad.imageoptions.*;
import com.itextpdf.text.pdf.PdfReader;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.tools.imageio.ImageIOUtil;
import org.apache.poi.EncryptedDocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.IntStream;
/**
* @author yudian-it
* @date 2017/11/13
*/
@Component
@DependsOn(ConfigConstants.BEAN_NAME)
public class FileHandlerService implements InitializingBean {
private static final String PDF2JPG_IMAGE_FORMAT = ".jpg";
private static final String PDF_PASSWORD_MSG = "password";
private final Logger logger = LoggerFactory.getLogger(FileHandlerService.class);
private final String fileDir = ConfigConstants.getFileDir();
private final CacheService cacheService;
@Value("${server.tomcat.uri-encoding:UTF-8}")
private String uriEncoding;
public FileHandlerService(CacheService cacheService) {
this.cacheService = cacheService;
}
/**
* @return 已转换过的文件集合(缓存)
*/
public Map<String, String> listConvertedFiles() {
return cacheService.getPDFCache();
}
/**
* @return 已转换过的文件,根据文件名获取
*/
public String getConvertedFile(String key) {
return cacheService.getPDFCache(key);
}
/**
* @param key pdf本地路径
* @return 已将pdf转换成图片的图片本地相对路径
*/
public Integer getPdf2jpgCache(String key) {
return cacheService.getPdfImageCache(key);
}
/**
* 从路径中获取文件负
*
* @param path 类似这种C:\Users\yudian-it\Downloads
* @return 文件名
*/
public String getFileNameFromPath(String path) {
return path.substring(path.lastIndexOf(File.separator) + 1);
}
/**
* 获取相对路径
*
* @param absolutePath 绝对路径
* @return 相对路径
*/
public String getRelativePath(String absolutePath) {
return absolutePath.substring(fileDir.length());
}
/**
* 添加转换后PDF缓存
*
* @param fileName pdf文件名
* @param value 缓存相对路径
*/
public void addConvertedFile(String fileName, String value) {
cacheService.putPDFCache(fileName, value);
}
/**
* 添加转换后图片组缓存
*
* @param pdfFilePath pdf文件绝对路径
* @param num 图片张数
*/
public void addPdf2jpgCache(String pdfFilePath, int num) {
cacheService.putPdfImageCache(pdfFilePath, num);
}
/**
* 获取redis中压缩包内图片文件
*
* @param compressFileKey compressFileKey
* @return 图片文件访问url列表
*/
public List<String> getImgCache(String compressFileKey) {
return cacheService.getImgCache(compressFileKey);
}
/**
* 设置redis中压缩包内图片文件
*
* @param fileKey fileKey
* @param imgs 图片文件访问url列表
*/
public void putImgCache(String fileKey, List<String> imgs) {
cacheService.putImgCache(fileKey, imgs);
}
/**
* cad定义线程池
*/
private ExecutorService pool = null;
@Override
public void afterPropertiesSet() throws Exception {
pool = Executors.newFixedThreadPool(ConfigConstants.getCadThread());
}
/**
* 对转换后的文件进行操作(改变编码方式)
*
* @param outFilePath 文件绝对路径
*/
public void doActionConvertedFile(String outFilePath) {
String charset = EncodingDetects.getJavaEncode(outFilePath);
StringBuilder sb = new StringBuilder();
try (InputStream inputStream = new FileInputStream(outFilePath); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset))) {
String line;
while (null != (line = reader.readLine())) {
if (line.contains("charset=gb2312")) {
line = line.replace("charset=gb2312", "charset=utf-8");
}
sb.append(line);
}
// 添加sheet控制头
sb.append("<script src=\"js/jquery-3.6.1.min.js\" type=\"text/javascript\"></script>");
sb.append("<script src=\"excel/excel.header.js\" type=\"text/javascript\"></script>");
sb.append("<link rel=\"stylesheet\" href=\"excel/excel.css\">");
} catch (IOException e) {
e.printStackTrace();
}
// 重新写入文件
try (FileOutputStream fos = new FileOutputStream(outFilePath); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
writer.write(sb.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取本地 pdf 转 image 后的 web 访问地址
*
* @param pdfFilePath pdf文件名
* @param index 图片索引
* @return 图片访问地址
*/
private String getPdf2jpgUrl(String pdfFilePath, int index) {
String baseUrl = BaseUrlFilter.getBaseUrl();
pdfFilePath = pdfFilePath.replace(fileDir, "");
String pdfFolder = pdfFilePath.substring(0, pdfFilePath.length() - 4);
String urlPrefix;
try {
urlPrefix = baseUrl + URLEncoder.encode(pdfFolder, uriEncoding).replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException", e);
urlPrefix = baseUrl + pdfFolder;
}
return urlPrefix + "/" + index + PDF2JPG_IMAGE_FORMAT;
}
/**
* 获取缓存中的 pdf 转换成 jpg 图片集
*
* @param pdfFilePath pdf文件路径
* @return 图片访问集合
*/
private List<String> loadPdf2jpgCache(String pdfFilePath) {
List<String> imageUrls = new ArrayList<>();
Integer imageCount = this.getPdf2jpgCache(pdfFilePath);
if (Objects.isNull(imageCount)) {
return imageUrls;
}
IntStream.range(0, imageCount).forEach(i -> {
String imageUrl = this.getPdf2jpgUrl(pdfFilePath, i);
imageUrls.add(imageUrl);
});
return imageUrls;
}
/**
* pdf文件转换成jpg图片集
* fileNameFilePath pdf文件路径
* pdfFilePath pdf输出文件路径
* pdfName pdf文件名称
* loadPdf2jpgCache 图片访问集合
*/
public List<String> pdf2jpg(String fileNameFilePath, String pdfFilePath, String pdfName, FileAttribute fileAttribute) throws Exception {
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
boolean usePasswordCache = fileAttribute.getUsePasswordCache();
String filePassword = fileAttribute.getFilePassword();
String pdfPassword = null;
PDDocument doc = null;
PdfReader pdfReader = null;
if (!forceUpdatedCache) {
List<String> cacheResult = this.loadPdf2jpgCache(pdfFilePath);
if (!CollectionUtils.isEmpty(cacheResult)) {
return cacheResult;
}
}
List<String> imageUrls = new ArrayList<>();
try {
File pdfFile = new File(fileNameFilePath);
if (!pdfFile.exists()) {
return null;
}
doc = PDDocument.load(pdfFile, filePassword);
doc.setResourceCache(new NotResourceCache());
int pageCount = doc.getNumberOfPages();
PDFRenderer pdfRenderer = new PDFRenderer(doc);
int index = pdfFilePath.lastIndexOf(".");
String folder = pdfFilePath.substring(0, index);
File path = new File(folder);
if (!path.exists() && !path.mkdirs()) {
logger.error("创建转换文件【{}】目录失败,请检查目录权限!", folder);
}
String imageFilePath;
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
imageFilePath = folder + File.separator + pageIndex + PDF2JPG_IMAGE_FORMAT;
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, ConfigConstants.getPdf2JpgDpi(), ImageType.RGB);
ImageIOUtil.writeImage(image, imageFilePath, ConfigConstants.getPdf2JpgDpi());
String imageUrl = this.getPdf2jpgUrl(pdfFilePath, pageIndex);
imageUrls.add(imageUrl);
}
try {
if (!ObjectUtils.isEmpty(filePassword)) { //获取到密码 判断是否是加密文件
pdfReader = new PdfReader(fileNameFilePath); //读取PDF文件 通过异常获取该文件是否有密码字符
}
} catch (Exception e) { //获取异常方法 判断是否有加密字符串
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
for (Throwable throwable : throwableArray) {
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
if (e.getMessage().toLowerCase().contains(PDF_PASSWORD_MSG)) {
pdfPassword = PDF_PASSWORD_MSG; //查询到该文件是密码文件 输出带密码的值
}
}
}
if (!PDF_PASSWORD_MSG.equals(pdfPassword)) { //该文件异常 错误原因非密码原因输出错误
logger.error("Convert pdf exception, pdfFilePath{}", pdfFilePath, e);
}
} finally {
if (pdfReader != null) { //关闭
pdfReader.close();
}
}
if (usePasswordCache || !PDF_PASSWORD_MSG.equals(pdfPassword)) { //加密文件 判断是否启用缓存命令
this.addPdf2jpgCache(pdfFilePath, pageCount);
}
} catch (IOException e) {
if (!e.getMessage().contains(PDF_PASSWORD_MSG)) {
logger.error("Convert pdf to jpg exception, pdfFilePath{}", pdfFilePath, e);
}
throw new Exception(e);
} finally {
if (doc != null) { //关闭
doc.close();
}
}
return imageUrls;
}
/**
* cad文件转pdf
*
* @param inputFilePath cad文件路径
* @param outputFilePath pdf输出文件路径
* @return 转换是否成功
*/
public String cadToPdf(String inputFilePath, String outputFilePath, String cadPreviewType, FileAttribute fileAttribute) throws Exception {
final InterruptionTokenSource source = new InterruptionTokenSource();//CAD延时
final SvgOptions SvgOptions = new SvgOptions();
final PdfOptions pdfOptions = new PdfOptions();
final TiffOptions TiffOptions = new TiffOptions(TiffExpectedFormat.TiffJpegRgb);
if (fileAttribute.isCompressFile()) { //判断 是压缩包的创建新的目录
int index = outputFilePath.lastIndexOf("/"); //截取最后一个斜杠的前面的内容
String folder = outputFilePath.substring(0, index);
File path = new File(folder);
//目录不存在 创建新的目录
if (!path.exists()) {
path.mkdirs();
}
}
File outputFile = new File(outputFilePath);
try {
LoadOptions opts = new LoadOptions();
opts.setSpecifiedEncoding(CodePages.SimpChinese);
final Image cadImage = Image.load(inputFilePath, opts);
try {
RasterizationQuality rasterizationQuality = new RasterizationQuality();
rasterizationQuality.setArc(RasterizationQualityValue.High);
rasterizationQuality.setHatch(RasterizationQualityValue.High);
rasterizationQuality.setText(RasterizationQualityValue.High);
rasterizationQuality.setOle(RasterizationQualityValue.High);
rasterizationQuality.setObjectsPrecision(RasterizationQualityValue.High);
rasterizationQuality.setTextThicknessNormalization(true);
CadRasterizationOptions cadRasterizationOptions = new CadRasterizationOptions();
cadRasterizationOptions.setBackgroundColor(Color.getWhite());
cadRasterizationOptions.setPageWidth(cadImage.getWidth());
cadRasterizationOptions.setPageHeight(cadImage.getHeight());
cadRasterizationOptions.setUnitType(cadImage.getUnitType());
cadRasterizationOptions.setAutomaticLayoutsScaling(false);
cadRasterizationOptions.setNoScaling(false);
cadRasterizationOptions.setQuality(rasterizationQuality);
cadRasterizationOptions.setDrawType(CadDrawTypeMode.UseObjectColor);
cadRasterizationOptions.setExportAllLayoutContent(true);
cadRasterizationOptions.setVisibilityMode(VisibilityMode.AsScreen);
switch (cadPreviewType) { //新增格式方法
case "svg":
SvgOptions.setVectorRasterizationOptions(cadRasterizationOptions);
SvgOptions.setInterruptionToken(source.getToken());
break;
case "pdf":
pdfOptions.setVectorRasterizationOptions(cadRasterizationOptions);
pdfOptions.setInterruptionToken(source.getToken());
break;
case "tif":
TiffOptions.setVectorRasterizationOptions(cadRasterizationOptions);
TiffOptions.setInterruptionToken(source.getToken());
break;
}
Callable<String> call = () -> {
try (OutputStream stream = new FileOutputStream(outputFile)) {
switch (cadPreviewType) {
case "svg":
cadImage.save(stream, SvgOptions);
break;
case "pdf":
cadImage.save(stream, pdfOptions);
break;
case "tif":
cadImage.save(stream, TiffOptions);
break;
}
} catch (IOException e) {
logger.error("CADFileNotFoundExceptioninputFilePath{}", inputFilePath, e);
return null;
} finally {
cadImage.dispose();
source.interrupt(); //结束任务
source.dispose();
}
return "true";
};
Future<String> result = pool.submit(call);
try {
result.get(Long.parseLong(ConfigConstants.getCadTimeout()), TimeUnit.SECONDS);
// 如果在超时时间内没有数据返回则抛出TimeoutException异常
} catch (InterruptedException e) {
logger.error("CAD转换文件异常", e);
return null;
} catch (ExecutionException e) {
logger.error("CAD转换在尝试取得任务结果时出错", e);
return null;
} catch (TimeoutException e) {
logger.error("CAD转换时间超时", e);
return null;
} finally {
source.interrupt(); //结束任务
source.dispose();
cadImage.dispose();
// pool.shutdownNow();
}
} finally {
source.dispose();
cadImage.dispose();
}
} finally {
source.dispose();
}
return "true";
}
/**
* @param str 原字符串(待截取原串)
* @param posStr 指定字符串
* @return 截取截取指定字符串之后的数据
*/
public static String getSubString(String str, String posStr) {
return str.substring(str.indexOf(posStr) + posStr.length());
}
/**
* 获取文件属性
*
* @param url url
* @return 文件属性
*/
public FileAttribute getFileAttribute(String url, HttpServletRequest req) {
FileAttribute attribute = new FileAttribute();
String suffix;
FileType type;
String originFileName; //原始文件名
String outFilePath; //生成文件的路径
String originFilePath; //原始文件路径
String fullFileName = WebUtils.getUrlParameterReg(url, "fullfilename");
String compressFileKey = WebUtils.getUrlParameterReg(url, "kkCompressfileKey"); //压缩包获取文件名
String compressFilePath = WebUtils.getUrlParameterReg(url, "kkCompressfilepath"); //压缩包获取文件路径
if (StringUtils.hasText(fullFileName)) {
originFileName = fullFileName;
type = FileType.typeFromFileName(fullFileName);
suffix = KkFileUtils.suffixFromFileName(fullFileName);
// 移除fullfilename参数
url = WebUtils.clearFullfilenameParam(url);
} else {
originFileName = WebUtils.getFileNameFromURL(url);
type = FileType.typeFromUrl(url);
suffix = WebUtils.suffixFromUrl(url);
}
boolean isCompressFile = !ObjectUtils.isEmpty(compressFileKey);
if (isCompressFile) { //判断是否使用特定压缩包符号
try {
// http://127.0.0.1:8012/各类型文件1 - 副本.zip_/各类型文件/正常预览/PPT转的PDF.pdf?kkCompressfileKey=各类型文件1 - 副本.zip_
// http://127.0.0.1:8012/preview/各类型文件1 - 副本.zip_/各类型文件/正常预览/PPT转的PDF.pdf?kkCompressfileKey=各类型文件1 - 副本.zip_ 获取路径就会错误 需要下面的方法
String urlStrr = getSubString(compressFilePath, compressFileKey); //反代情况下添加前缀,只获取有压缩包字符的路径
originFileName = compressFileKey + urlStrr.trim(); //拼接完整路径
originFileName = URLDecoder.decode(originFileName, uriEncoding); //压缩包文件中文编码问题
attribute.setSkipDownLoad(true);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
if (UrlEncoderUtils.hasUrlEncoded(originFileName)) { //判断文件名是否转义
try {
originFileName = URLDecoder.decode(originFileName, uriEncoding); //转义的文件名 解下出原始文件名
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}else {
url = WebUtils.encodeUrlFileName(url); //对未转义的url进行转义
}
originFileName = KkFileUtils.htmlEscape(originFileName); //文件名处理
boolean isHtmlView = 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");
String cacheFilePrefixName = null;
try {
cacheFilePrefixName = originFileName.substring(0, originFileName.lastIndexOf(".")) + suffix + "."; //这里统一文件名处理 下面更具类型 各自添加后缀
} catch (Exception e) {
logger.error("获取文件名后缀错误:", e);
// e.printStackTrace();
}
String cacheFileName = this.getCacheFileName(type, originFileName, cacheFilePrefixName, isHtmlView, isCompressFile);
outFilePath = fileDir + cacheFileName;
originFilePath = fileDir + originFileName;
String cacheListName = cacheFilePrefixName + "ListName"; //文件列表缓存文件名
attribute.setType(type);
attribute.setName(originFileName);
attribute.setCacheName(cacheFileName);
attribute.setCacheListName(cacheListName);
attribute.setHtmlView(isHtmlView);
attribute.setOutFilePath(outFilePath);
attribute.setOriginFilePath(originFilePath);
attribute.setSuffix(suffix);
attribute.setUrl(url);
if (req != null) {
String officePreviewType = req.getParameter("officePreviewType");
String forceUpdatedCache = req.getParameter("forceUpdatedCache");
String usePasswordCache = req.getParameter("usePasswordCache");
if (StringUtils.hasText(officePreviewType)) {
attribute.setOfficePreviewType(officePreviewType);
}
if (StringUtils.hasText(compressFileKey)) {
attribute.setCompressFile(isCompressFile);
attribute.setCompressFileKey(compressFileKey);
}
if ("true".equalsIgnoreCase(forceUpdatedCache)) {
attribute.setForceUpdatedCache(true);
}
String tifPreviewType = req.getParameter("tifPreviewType");
if (StringUtils.hasText(tifPreviewType)) {
attribute.setTifPreviewType(tifPreviewType);
}
String filePassword = req.getParameter("filePassword");
if (StringUtils.hasText(filePassword)) {
attribute.setFilePassword(filePassword);
}
if ("true".equalsIgnoreCase(usePasswordCache)) {
attribute.setUsePasswordCache(true);
}
String kkProxyAuthorization = req.getHeader("kk-proxy-authorization");
attribute.setKkProxyAuthorization(kkProxyAuthorization);
}
return attribute;
}
/**
* 获取缓存的文件名
*
* @return 文件名
*/
private String getCacheFileName(FileType type, String originFileName, String cacheFilePrefixName, boolean isHtmlView, boolean isCompressFile) {
String cacheFileName;
if (type.equals(FileType.OFFICE)) {
cacheFileName = cacheFilePrefixName + (isHtmlView ? "html" : "pdf"); //生成文件添加类型后缀 防止同名文件
} else if (type.equals(FileType.PDF)) {
cacheFileName = originFileName;
} else if (type.equals(FileType.MEDIACONVERT)) {
cacheFileName = cacheFilePrefixName + "mp4";
} else if (type.equals(FileType.CAD)) {
String cadPreviewType = ConfigConstants.getCadPreviewType();
cacheFileName = cacheFilePrefixName + cadPreviewType; //生成文件添加类型后缀 防止同名文件
} else if (type.equals(FileType.COMPRESS)) {
cacheFileName = originFileName;
} else if (type.equals(FileType.TIFF)) {
cacheFileName = cacheFilePrefixName + ConfigConstants.getTifPreviewType();
} else {
cacheFileName = originFileName;
}
if (isCompressFile) { //判断是否使用特定压缩包符号
cacheFileName = "_decompression" + cacheFileName;
}
return cacheFileName;
}
/**
* @return 已转换过的视频文件集合(缓存)
*/
public Map<String, String> listConvertedMedias() {
return cacheService.getMediaConvertCache();
}
/**
* 添加转换后的视频文件缓存
*
* @param fileName
* @param value
*/
public void addConvertedMedias(String fileName, String value) {
cacheService.putMediaConvertCache(fileName, value);
}
/**
* @return 已转换视频文件缓存,根据文件名获取
*/
public String getConvertedMedias(String key) {
return cacheService.getMediaConvertCache(key);
}
}

View File

@@ -0,0 +1,38 @@
package cn.keking.service;
import cn.keking.model.FileAttribute;
import org.springframework.ui.Model;
/**
* Created by kl on 2018/1/17.
* Content :
*/
public interface FilePreview {
String PDF_FILE_PREVIEW_PAGE = "pdf";
String PPT_FILE_PREVIEW_PAGE = "ppt";
String COMPRESS_FILE_PREVIEW_PAGE = "compress";
String MEDIA_FILE_PREVIEW_PAGE = "media";
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 ONLINE3D_PREVIEW_PAGE = "online3D";
String EPUB_PREVIEW_PAGE = "epub";
String XMIND_FILE_PREVIEW_PAGE = "xmind";
String EML_FILE_PREVIEW_PAGE = "eml";
String OFFICE_PICTURE_FILE_PREVIEW_PAGE = "officePicture";
String TXT_FILE_PREVIEW_PAGE = "txt";
String CODE_FILE_PREVIEW_PAGE = "code";
String EXEL_FILE_PREVIEW_PAGE = "html";
String XML_FILE_PREVIEW_PAGE = "xml";
String MARKDOWN_FILE_PREVIEW_PAGE = "markdown";
String BPMN_FILE_PREVIEW_PAGE = "bpmn";
String DCM_FILE_PREVIEW_PAGE = "dcm";
String DRAWUI_FILE_PREVIEW_PAGE = "drawio";
String NOT_SUPPORTED_FILE_PAGE = "fileNotSupported";
String XLSX_FILE_PREVIEW_PAGE = "officeweb";
String CSV_FILE_PREVIEW_PAGE = "csv";
String filePreviewHandle(String url, Model model, FileAttribute fileAttribute);
}

View File

@@ -0,0 +1,23 @@
package cn.keking.service;
import cn.keking.model.FileAttribute;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
/**
* Created by kl on 2018/1/17.
* Content :
*/
@Service
public class FilePreviewFactory {
private final ApplicationContext context;
public FilePreviewFactory(ApplicationContext context) {
this.context = context;
}
public FilePreview get(FileAttribute fileAttribute) {
return context.getBean(fileAttribute.getType().getInstanceName(), FilePreview.class);
}
}

View File

@@ -0,0 +1,144 @@
package cn.keking.service;
import cn.keking.utils.LocalOfficeUtils;
import org.apache.commons.lang3.StringUtils;
import org.jodconverter.core.office.InstalledOfficeManagerHolder;
import org.jodconverter.core.office.OfficeException;
import org.jodconverter.core.office.OfficeUtils;
import org.jodconverter.core.util.OSUtils;
import org.jodconverter.local.office.LocalOfficeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.convert.DurationStyle;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* 创建文件转换器
*
* @author chenjh
* @since 2022-12-15
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class OfficePluginManager {
private final Logger logger = LoggerFactory.getLogger(OfficePluginManager.class);
private LocalOfficeManager officeManager;
@Value("${office.plugin.server.ports:2001,2002}")
private String serverPorts;
@Value("${office.plugin.task.timeout:5m}")
private String timeOut;
@Value("${office.plugin.task.taskexecutiontimeout:5m}")
private String taskExecutionTimeout;
@Value("${office.plugin.task.maxtasksperprocess:5}")
private int maxTasksPerProcess;
/**
* 启动Office组件进程
*/
@PostConstruct
public void startOfficeManager() throws OfficeException {
File officeHome = LocalOfficeUtils.getDefaultOfficeHome();
if (officeHome == null) {
throw new RuntimeException("找不到office组件请确认'office.home'配置是否有误");
}
boolean killOffice = killProcess();
if (killOffice) {
logger.warn("检测到有正在运行的office进程已自动结束该进程");
}
try {
String[] portsString = serverPorts.split(",");
int[] ports = Arrays.stream(portsString).mapToInt(Integer::parseInt).toArray();
long timeout = DurationStyle.detectAndParse(timeOut).toMillis();
long taskexecutiontimeout = DurationStyle.detectAndParse(taskExecutionTimeout).toMillis();
officeManager = LocalOfficeManager.builder()
.officeHome(officeHome)
.portNumbers(ports)
.processTimeout(timeout)
.maxTasksPerProcess(maxTasksPerProcess)
.taskExecutionTimeout(taskexecutiontimeout)
.build();
officeManager.start();
InstalledOfficeManagerHolder.setInstance(officeManager);
} catch (Exception e) {
logger.error("启动office组件失败请检查office组件是否可用");
throw e;
}
}
private boolean killProcess() {
boolean flag = false;
try {
if (OSUtils.IS_OS_WINDOWS) {
Process p = Runtime.getRuntime().exec("cmd /c tasklist ");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream os = p.getInputStream();
byte[] b = new byte[256];
while (os.read(b) > 0) {
baos.write(b);
}
String s = baos.toString();
if (s.contains("soffice.bin")) {
Runtime.getRuntime().exec("taskkill /im " + "soffice.bin" + " /f");
flag = true;
}
} else if (OSUtils.IS_OS_MAC || OSUtils.IS_OS_MAC_OSX) {
Process p = Runtime.getRuntime().exec(new String[]{"sh", "-c", "ps -ef | grep " + "soffice.bin"});
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream os = p.getInputStream();
byte[] b = new byte[256];
while (os.read(b) > 0) {
baos.write(b);
}
String s = baos.toString();
if (StringUtils.ordinalIndexOf(s, "soffice.bin", 3) > 0) {
String[] cmd = {"sh", "-c", "kill -15 `ps -ef|grep " + "soffice.bin" + "|awk 'NR==1{print $2}'`"};
Runtime.getRuntime().exec(cmd);
flag = true;
}
} else {
Process p = Runtime.getRuntime().exec(new String[]{"sh", "-c", "ps -ef | grep " + "soffice.bin" + " |grep -v grep | wc -l"});
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream os = p.getInputStream();
byte[] b = new byte[256];
while (os.read(b) > 0) {
baos.write(b);
}
String s = baos.toString();
if (!s.startsWith("0")) {
String[] cmd = {"sh", "-c", "ps -ef | grep soffice.bin | grep -v grep | awk '{print \"kill -9 \"$2}' | sh"};
Runtime.getRuntime().exec(cmd);
flag = true;
}
}
} catch (IOException e) {
logger.error("检测office进程异常", e);
}
return flag;
}
@PreDestroy
public void destroyOfficeManager() {
if (null != officeManager && officeManager.isRunning()) {
logger.info("Shutting down office process");
OfficeUtils.stopQuietly(officeManager);
}
}
}

View File

@@ -0,0 +1,100 @@
package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import com.sun.star.document.UpdateDocMode;
import org.apache.commons.lang3.StringUtils;
import org.jodconverter.core.office.OfficeException;
import org.jodconverter.local.LocalConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
/**
* @author yudian-it
*/
@Component
public class OfficeToPdfService {
private final static Logger logger = LoggerFactory.getLogger(OfficeToPdfService.class);
public void openOfficeToPDF(String inputFilePath, String outputFilePath, FileAttribute fileAttribute) throws OfficeException {
office2pdf(inputFilePath, outputFilePath, fileAttribute);
}
public static void converterFile(File inputFile, String outputFilePath_end, FileAttribute fileAttribute) throws OfficeException {
File outputFile = new File(outputFilePath_end);
// 假如目标路径不存在,则新建该路径
if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) {
logger.error("创建目录【{}】失败,请检查目录权限!",outputFilePath_end);
}
LocalConverter.Builder builder;
Map<String, Object> filterData = new HashMap<>();
filterData.put("EncryptFile", true);
if(!ConfigConstants.getOfficePageRange().equals("false")){
filterData.put("PageRange", ConfigConstants.getOfficePageRange()); //限制页面
}
if(!ConfigConstants.getOfficeWatermark().equals("false")){
filterData.put("Watermark", ConfigConstants.getOfficeWatermark()); //水印
}
filterData.put("Quality", ConfigConstants.getOfficeQuality()); //图片压缩
filterData.put("MaxImageResolution", ConfigConstants.getOfficeMaxImageResolution()); //DPI
if(ConfigConstants.getOfficeExportBookmarks()){
filterData.put("ExportBookmarks", true); //导出书签
}
if(ConfigConstants.getOfficeExportNotes()){
filterData.put("ExportNotes", true); //批注作为PDF的注释
}
if(ConfigConstants.getOfficeDocumentOpenPasswords()){
filterData.put("DocumentOpenPassword", fileAttribute.getFilePassword()); //给PDF添加密码
}
Map<String, Object> customProperties = new HashMap<>();
customProperties.put("FilterData", filterData);
if (StringUtils.isNotBlank(fileAttribute.getFilePassword())) {
Map<String, Object> loadProperties = new HashMap<>();
loadProperties.put("Hidden", true);
loadProperties.put("ReadOnly", true);
loadProperties.put("UpdateDocMode", UpdateDocMode.NO_UPDATE);
loadProperties.put("Password", fileAttribute.getFilePassword());
builder = LocalConverter.builder().loadProperties(loadProperties).storeProperties(customProperties);
} else {
builder = LocalConverter.builder().storeProperties(customProperties);
}
builder.build().convert(inputFile).to(outputFile).execute();
}
public void office2pdf(String inputFilePath, String outputFilePath, FileAttribute fileAttribute) throws OfficeException {
if (null != inputFilePath) {
File inputFile = new File(inputFilePath);
// 判断目标文件路径是否为空
if (null == outputFilePath) {
// 转换后的文件路径
String outputFilePath_end = getOutputFilePath(inputFilePath);
if (inputFile.exists()) {
// 找不到源文件, 则返回
converterFile(inputFile, outputFilePath_end, fileAttribute);
}
} else {
if (inputFile.exists()) {
// 找不到源文件, 则返回
converterFile(inputFile, outputFilePath, fileAttribute);
}
}
}
}
public static String getOutputFilePath(String inputFilePath) {
return inputFilePath.replaceAll("."+ getPostfix(inputFilePath), ".pdf");
}
public static String getPostfix(String inputFilePath) {
return inputFilePath.substring(inputFilePath.lastIndexOf(".") + 1);
}
}

View File

@@ -0,0 +1,20 @@
package cn.keking.service;
import java.util.List;
public class ZtreeNodeVo {
public String id;
public String pid;
public String name;
public List<ZtreeNodeVo> children;
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPid(String pid) {
this.pid = pid;
}
public void setChildren(List<ZtreeNodeVo> children) {
this.children = children;
}
}

View File

@@ -0,0 +1,42 @@
package cn.keking.service.cache;
import java.util.List;
import java.util.Map;
/**
* @author: chenjh
* @since: 2019/4/2 16:45
*/
public interface CacheService {
String FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file";
String FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合
String FILE_PREVIEW_PDF_IMGS_KEY = "converted-preview-pdfimgs-file";
String FILE_PREVIEW_MEDIA_CONVERT_KEY = "converted-preview-media-file";
String TASK_QUEUE_NAME = "convert-task";
Integer DEFAULT_PDF_CAPACITY = 500000;
Integer DEFAULT_IMG_CAPACITY = 500000;
Integer DEFAULT_PDFIMG_CAPACITY = 500000;
Integer DEFAULT_MEDIACONVERT_CAPACITY = 500000;
void initPDFCachePool(Integer capacity);
void initIMGCachePool(Integer capacity);
void initPdfImagesCachePool(Integer capacity);
void initMediaConvertCachePool(Integer capacity);
void putPDFCache(String key, String value);
void putImgCache(String key, List<String> value);
Map<String, String> getPDFCache();
String getPDFCache(String key);
Map<String, List<String>> getImgCache();
List<String> getImgCache(String key);
Integer getPdfImageCache(String key);
void putPdfImageCache(String pdfFilePath, int num);
Map<String, String> getMediaConvertCache();
void putMediaConvertCache(String key, String value);
String getMediaConvertCache(String key);
void cleanCache();
void addQueueTask(String url);
String takeQueueTask() throws InterruptedException;
}

View File

@@ -0,0 +1,20 @@
package cn.keking.service.cache;
import org.apache.pdfbox.cos.COSObject;
import org.apache.pdfbox.pdmodel.DefaultResourceCache;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import java.io.IOException;
/**
* @author: Sawyer.Yong
* @since: 2023/02/18 14:45
* 解决图片 SoftReference 导致内存无法被回收导致的OOM, 详见 https://issues.apache.org/jira/browse/PDFBOX-3700
*/
public class NotResourceCache extends DefaultResourceCache {
@Override
public void put(COSObject indirect, PDXObject xobject) throws IOException {
// do nothing
}
}

View File

@@ -0,0 +1,145 @@
package cn.keking.service.cache.impl;
import cn.keking.service.cache.CacheService;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.Weighers;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* @auther: chenjh
* @time: 2019/4/2 17:21
* @description
*/
@Service
@ConditionalOnExpression("'${cache.type:default}'.equals('jdk')")
public class CacheServiceJDKImpl implements CacheService {
private Map<String, String> pdfCache;
private Map<String, List<String>> imgCache;
private Map<String, Integer> pdfImagesCache;
private Map<String, String> mediaConvertCache;
private static final int QUEUE_SIZE = 500000;
private final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
@PostConstruct
public void initCache(){
initPDFCachePool(CacheService.DEFAULT_PDF_CAPACITY);
initIMGCachePool(CacheService.DEFAULT_IMG_CAPACITY);
initPdfImagesCachePool(CacheService.DEFAULT_PDFIMG_CAPACITY);
initMediaConvertCachePool(CacheService.DEFAULT_MEDIACONVERT_CAPACITY);
}
@Override
public void putPDFCache(String key, String value) {
pdfCache.put(key, value);
}
@Override
public void putImgCache(String key, List<String> value) {
imgCache.put(key, value);
}
@Override
public Map<String, String> getPDFCache() {
return pdfCache;
}
@Override
public String getPDFCache(String key) {
return pdfCache.get(key);
}
@Override
public Map<String, List<String>> getImgCache() {
return imgCache;
}
@Override
public List<String> getImgCache(String key) {
if(StringUtils.isEmpty(key)){
return new ArrayList<>();
}
return imgCache.get(key);
}
@Override
public Integer getPdfImageCache(String key) {
return pdfImagesCache.get(key);
}
@Override
public void putPdfImageCache(String pdfFilePath, int num) {
pdfImagesCache.put(pdfFilePath, num);
}
@Override
public Map<String, String> getMediaConvertCache() {
return mediaConvertCache;
}
@Override
public void putMediaConvertCache(String key, String value) {
mediaConvertCache.put(key, value);
}
@Override
public String getMediaConvertCache(String key) {
return mediaConvertCache.get(key);
}
@Override
public void cleanCache() {
initPDFCachePool(CacheService.DEFAULT_PDF_CAPACITY);
initIMGCachePool(CacheService.DEFAULT_IMG_CAPACITY);
initPdfImagesCachePool(CacheService.DEFAULT_PDFIMG_CAPACITY);
initMediaConvertCachePool(CacheService.DEFAULT_MEDIACONVERT_CAPACITY);
}
@Override
public void addQueueTask(String url) {
blockingQueue.add(url);
}
@Override
public String takeQueueTask() throws InterruptedException {
return blockingQueue.take();
}
@Override
public void initPDFCachePool(Integer capacity) {
pdfCache = new ConcurrentLinkedHashMap.Builder<String, String>()
.maximumWeightedCapacity(capacity).weigher(Weighers.singleton())
.build();
}
@Override
public void initIMGCachePool(Integer capacity) {
imgCache = new ConcurrentLinkedHashMap.Builder<String, List<String>>()
.maximumWeightedCapacity(capacity).weigher(Weighers.singleton())
.build();
}
@Override
public void initPdfImagesCachePool(Integer capacity) {
pdfImagesCache = new ConcurrentLinkedHashMap.Builder<String, Integer>()
.maximumWeightedCapacity(capacity).weigher(Weighers.singleton())
.build();
}
@Override
public void initMediaConvertCachePool(Integer capacity) {
mediaConvertCache = new ConcurrentLinkedHashMap.Builder<String, String>()
.maximumWeightedCapacity(capacity).weigher(Weighers.singleton())
.build();
}
}

View File

@@ -0,0 +1,144 @@
package cn.keking.service.cache.impl;
import cn.keking.service.cache.CacheService;
import org.redisson.Redisson;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* @auther: chenjh
* @time: 2019/4/2 18:02
* @description
*/
@ConditionalOnExpression("'${cache.type:default}'.equals('redis')")
@Service
public class CacheServiceRedisImpl implements CacheService {
private final RedissonClient redissonClient;
public CacheServiceRedisImpl(Config config) {
this.redissonClient = Redisson.create(config);
}
@Override
public void initPDFCachePool(Integer capacity) { }
@Override
public void initIMGCachePool(Integer capacity) { }
@Override
public void initPdfImagesCachePool(Integer capacity) { }
@Override
public void initMediaConvertCachePool(Integer capacity) {
}
@Override
public void putPDFCache(String key, String value) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
convertedList.fastPut(key, value);
}
@Override
public void putImgCache(String key, List<String> value) {
RMapCache<String, List<String>> convertedList = redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
convertedList.fastPut(key, value);
}
@Override
public Map<String, String> getPDFCache() {
return redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
}
@Override
public String getPDFCache(String key) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
return convertedList.get(key);
}
@Override
public Map<String, List<String>> getImgCache() {
return redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
}
@Override
public List<String> getImgCache(String key) {
RMapCache<String, List<String>> convertedList = redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
return convertedList.get(key);
}
@Override
public Integer getPdfImageCache(String key) {
RMapCache<String, Integer> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_IMGS_KEY);
return convertedList.get(key);
}
@Override
public void putPdfImageCache(String pdfFilePath, int num) {
RMapCache<String, Integer> convertedList = redissonClient.getMapCache(FILE_PREVIEW_PDF_IMGS_KEY);
convertedList.fastPut(pdfFilePath, num);
}
@Override
public Map<String, String> getMediaConvertCache() {
return redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
}
@Override
public void putMediaConvertCache(String key, String value) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
convertedList.fastPut(key, value);
}
@Override
public String getMediaConvertCache(String key) {
RMapCache<String, String> convertedList = redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
return convertedList.get(key);
}
@Override
public void cleanCache() {
cleanPdfCache();
cleanImgCache();
cleanPdfImgCache();
cleanMediaConvertCache();
}
@Override
public void addQueueTask(String url) {
RBlockingQueue<String> queue = redissonClient.getBlockingQueue(TASK_QUEUE_NAME);
queue.addAsync(url);
}
@Override
public String takeQueueTask() throws InterruptedException {
RBlockingQueue<String> queue = redissonClient.getBlockingQueue(TASK_QUEUE_NAME);
return queue.take();
}
private void cleanPdfCache() {
RMapCache<String, String> pdfCache = redissonClient.getMapCache(FILE_PREVIEW_PDF_KEY);
pdfCache.clear();
}
private void cleanImgCache() {
RMapCache<String, List<String>> imgCache = redissonClient.getMapCache(FILE_PREVIEW_IMGS_KEY);
imgCache.clear();
}
private void cleanPdfImgCache() {
RMapCache<String, Integer> pdfImg = redissonClient.getMapCache(FILE_PREVIEW_PDF_IMGS_KEY);
pdfImg.clear();
}
private void cleanMediaConvertCache() {
RMapCache<String, Integer> mediaConvertCache = redissonClient.getMapCache(FILE_PREVIEW_MEDIA_CONVERT_KEY);
mediaConvertCache.clear();
}
}

View File

@@ -0,0 +1,290 @@
package cn.keking.service.cache.impl;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.ConfigUtils;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* @auther: chenjh
* @time: 2019/4/22 11:02
* @description
*/
@ConditionalOnExpression("'${cache.type:default}'.equals('default')")
@Service
public class CacheServiceRocksDBImpl implements CacheService {
static {
RocksDB.loadLibrary();
}
private static final String DB_PATH = ConfigUtils.getHomePath() + File.separator + "cache";
private static final int QUEUE_SIZE = 500000;
private static final Logger LOGGER = LoggerFactory.getLogger(CacheServiceRocksDBImpl.class);
private final BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(QUEUE_SIZE);
private RocksDB db;
{
try {
db = RocksDB.open(DB_PATH);
if (db.get(FILE_PREVIEW_PDF_KEY.getBytes()) == null) {
Map<String, String> initPDFCache = new HashMap<>();
db.put(FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(initPDFCache));
}
if (db.get(FILE_PREVIEW_IMGS_KEY.getBytes()) == null) {
Map<String, List<String>> initIMGCache = new HashMap<>();
db.put(FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(initIMGCache));
}
if (db.get(FILE_PREVIEW_PDF_IMGS_KEY.getBytes()) == null) {
Map<String, Integer> initPDFIMGCache = new HashMap<>();
db.put(FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(initPDFIMGCache));
}
} catch (RocksDBException | IOException e) {
LOGGER.error("Uable to init RocksDB" + e);
}
}
@Override
public void initPDFCachePool(Integer capacity) {
}
@Override
public void initIMGCachePool(Integer capacity) {
}
@Override
public void initPdfImagesCachePool(Integer capacity) {
}
@Override
public void initMediaConvertCachePool(Integer capacity) {
}
@Override
public void putPDFCache(String key, String value) {
try {
Map<String, String> pdfCacheItem = getPDFCache();
pdfCacheItem.put(key, value);
db.put(FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(pdfCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
}
@Override
public void putImgCache(String key, List<String> value) {
try {
Map<String, List<String>> imgCacheItem = getImgCache();
imgCacheItem.put(key, value);
db.put(FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(imgCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String> getPDFCache() {
Map<String, String> result = new HashMap<>();
try {
result = (Map<String, String>) toObject(db.get(FILE_PREVIEW_PDF_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public String getPDFCache(String key) {
String result = "";
try {
Map<String, String> map = (Map<String, String>) toObject(db.get(FILE_PREVIEW_PDF_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public Map<String, List<String>> getImgCache() {
Map<String, List<String>> result = new HashMap<>();
try {
result = (Map<String, List<String>>) toObject(db.get(FILE_PREVIEW_IMGS_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public List<String> getImgCache(String key) {
List<String> result = new ArrayList<>();
Map<String, List<String>> map;
try {
map = (Map<String, List<String>>) toObject(db.get(FILE_PREVIEW_IMGS_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
@SuppressWarnings("unchecked")
public Integer getPdfImageCache(String key) {
Integer result = 0;
Map<String, Integer> map;
try {
map = (Map<String, Integer>) toObject(db.get(FILE_PREVIEW_PDF_IMGS_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
public void putPdfImageCache(String pdfFilePath, int num) {
try {
Map<String, Integer> pdfImageCacheItem = getPdfImageCaches();
pdfImageCacheItem.put(pdfFilePath, num);
db.put(FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(pdfImageCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String> getMediaConvertCache() {
Map<String, String> result = new HashMap<>();
try {
result = (Map<String, String>) toObject(db.get(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
public void putMediaConvertCache(String key, String value) {
try {
Map<String, String> mediaConvertCacheItem = getMediaConvertCache();
mediaConvertCacheItem.put(key, value);
db.put(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes(), toByteArray(mediaConvertCacheItem));
} catch (RocksDBException | IOException e) {
LOGGER.error("Put into RocksDB Exception" + e);
}
}
@Override
@SuppressWarnings("unchecked")
public String getMediaConvertCache(String key) {
String result = "";
try {
Map<String, String> map = (Map<String, String>) toObject(db.get(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes()));
result = map.get(key);
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return result;
}
@Override
public void cleanCache() {
try {
cleanPdfCache();
cleanImgCache();
cleanPdfImgCache();
cleanMediaConvertCache();
} catch (IOException | RocksDBException e) {
LOGGER.error("Clean Cache Exception" + e);
}
}
@Override
public void addQueueTask(String url) {
blockingQueue.add(url);
}
@Override
public String takeQueueTask() throws InterruptedException {
return blockingQueue.take();
}
@SuppressWarnings("unchecked")
private Map<String, Integer> getPdfImageCaches() {
Map<String, Integer> map = new HashMap<>();
try {
map = (Map<String, Integer>) toObject(db.get(FILE_PREVIEW_PDF_IMGS_KEY.getBytes()));
} catch (RocksDBException | IOException | ClassNotFoundException e) {
LOGGER.error("Get from RocksDB Exception" + e);
}
return map;
}
private byte[] toByteArray(Object obj) throws IOException {
byte[] bytes;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
oos.close();
bos.close();
return bytes;
}
private Object toObject(byte[] bytes) throws IOException, ClassNotFoundException {
Object obj;
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
ois.close();
bis.close();
return obj;
}
private void cleanPdfCache() throws IOException, RocksDBException {
Map<String, String> initPDFCache = new HashMap<>();
db.put(FILE_PREVIEW_PDF_KEY.getBytes(), toByteArray(initPDFCache));
}
private void cleanImgCache() throws IOException, RocksDBException {
Map<String, List<String>> initIMGCache = new HashMap<>();
db.put(FILE_PREVIEW_IMGS_KEY.getBytes(), toByteArray(initIMGCache));
}
private void cleanPdfImgCache() throws IOException, RocksDBException {
Map<String, Integer> initPDFIMGCache = new HashMap<>();
db.put(FILE_PREVIEW_PDF_IMGS_KEY.getBytes(), toByteArray(initPDFIMGCache));
}
private void cleanMediaConvertCache() throws IOException, RocksDBException {
Map<String, String> initMediaConvertCache = new HashMap<>();
db.put(FILE_PREVIEW_MEDIA_CONVERT_KEY.getBytes(), toByteArray(initMediaConvertCache));
}
}

View File

@@ -0,0 +1,27 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
/**
* @author kl (http://kailing.pub)
* @since 2023/3/9
*/
@Component
public class BpmnFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public BpmnFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
model.addAttribute("fileName", fileAttribute.getName());
return FilePreview.BPMN_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,87 @@
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.KkFileUtils;
import cn.keking.utils.WebUtils;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import static cn.keking.service.impl.OfficeFilePreviewImpl.getPreviewType;
/**
* @author chenjh
* @since 2019/11/21 14:28
*/
@Service
public class CadFilePreviewImpl implements FilePreview {
private static final String OFFICE_PREVIEW_TYPE_IMAGE = "image";
private static final String OFFICE_PREVIEW_TYPE_ALL_IMAGES = "allImages";
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public CadFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 预览Type参数传了就取参数的没传取系统默认
String officePreviewType = fileAttribute.getOfficePreviewType() == null ? ConfigConstants.getOfficePreviewType() : fileAttribute.getOfficePreviewType();
String baseUrl = BaseUrlFilter.getBaseUrl();
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
String fileName = fileAttribute.getName();
String cadPreviewType = ConfigConstants.getCadPreviewType();
String cacheName = fileAttribute.getCacheName();
String outFilePath = fileAttribute.getOutFilePath();
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(cacheName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
String filePath = response.getContent();
String imageUrls = null;
if (StringUtils.hasText(outFilePath)) {
try {
imageUrls = fileHandlerService.cadToPdf(filePath, outFilePath, cadPreviewType, fileAttribute);
} catch (Exception e) {
e.printStackTrace();
}
if (imageUrls == null) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "CAD转换异常请联系管理员");
}
//是否保留CAD源文件
if (!fileAttribute.isCompressFile() && ConfigConstants.getDeleteSourceFile()) {
KkFileUtils.deleteFileByPath(filePath);
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileHandlerService.addConvertedFile(cacheName, fileHandlerService.getRelativePath(outFilePath));
}
}
}
cacheName= WebUtils.encodeFileName(cacheName);
if ("tif".equalsIgnoreCase(cadPreviewType)) {
model.addAttribute("currentUrl", cacheName);
return TIFF_FILE_PREVIEW_PAGE;
} else if ("svg".equalsIgnoreCase(cadPreviewType)) {
model.addAttribute("currentUrl", cacheName);
return SVG_FILE_PREVIEW_PAGE;
}
if (baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) {
return getPreviewType(model, fileAttribute, officePreviewType, cacheName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE, otherFilePreview);
}
model.addAttribute("pdfUrl", cacheName);
return PDF_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,26 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
/**
* @author kl (http://kailing.pub)
* @since 2021/2/18
*/
@Component
public class CodeFilePreviewImpl implements FilePreview {
private final SimTextFilePreviewImpl filePreviewHandle;
public CodeFilePreviewImpl(SimTextFilePreviewImpl filePreviewHandle) {
this.filePreviewHandle = filePreviewHandle;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
filePreviewHandle.filePreviewHandle(url, model, fileAttribute);
return CODE_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,48 @@
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.utils.KkFileUtils;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by kl on 2018/1/17.
* Content :图片文件处理
*/
@Component("commonPreview")
public class CommonPreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public CommonPreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, null);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
} else {
String file = fileHandlerService.getRelativePath(response.getContent());
model.addAttribute("currentUrl", file);
}
} else {
model.addAttribute("currentUrl", url);
}
return null;
}
}

View File

@@ -0,0 +1,82 @@
package cn.keking.service.impl;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FileHandlerService;
import cn.keking.service.CompressFileReader;
import cn.keking.utils.KkFileUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.EncryptedDocumentException;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.io.IOException;
/**
* Created by kl on 2018/1/17.
* Content :处理压缩包文件
*/
@Service
public class CompressFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final CompressFileReader compressFileReader;
private final OtherFilePreviewImpl otherFilePreview;
private static final String Rar_PASSWORD_MSG = "password";
public CompressFilePreviewImpl(FileHandlerService fileHandlerService, CompressFileReader compressFileReader, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.compressFileReader = compressFileReader;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName=fileAttribute.getName();
String filePassword = fileAttribute.getFilePassword();
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache();
String fileTree = null;
// 判断文件名是否存在(redis缓存读取)
if (forceUpdatedCache || !StringUtils.hasText(fileHandlerService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
String filePath = response.getContent();
try {
fileTree = compressFileReader.unRar(filePath, filePassword,fileName, fileAttribute);
} catch (Exception e) {
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
for (Throwable throwable : throwableArray) {
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
if (e.getMessage().toLowerCase().contains(Rar_PASSWORD_MSG)) {
model.addAttribute("needFilePassword", true);
return EXEL_FILE_PREVIEW_PAGE;
}
}
}
}
if (!ObjectUtils.isEmpty(fileTree)) {
//是否保留压缩包源文件
if (!fileAttribute.isCompressFile() && ConfigConstants.getDeleteSourceFile()) {
KkFileUtils.deleteFileByPath(filePath);
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileHandlerService.addConvertedFile(fileName, fileTree);
}
}else {
return otherFilePreview.notSupportedFile(model, fileAttribute, "压缩文件密码错误! 压缩文件损坏! 压缩文件类型不受支持!");
}
} else {
fileTree = fileHandlerService.getConvertedFile(fileName);
}
model.addAttribute("fileName", fileName);
model.addAttribute("fileTree", fileTree);
return COMPRESS_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,25 @@
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;
/**
* Dcm 文件处理
*/
@Service
public class DcmFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public DcmFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return DCM_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,25 @@
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;
/**
* Drawio 文件处理
*/
@Service
public class DrawioFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public DrawioFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return DRAWUI_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,25 @@
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;
/**
* EML 文件处理
*/
@Service
public class EmlFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public EmlFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return EML_FILE_PREVIEW_PAGE;
}
}

View File

@@ -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 EpubFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public EpubFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return EPUB_PREVIEW_PAGE;
}
}

View File

@@ -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;
/**
* @author kl (http://kailing.pub)
* @since 2020/12/25
*/
@Service
public class MarkdownFilePreviewImpl implements FilePreview {
private final SimTextFilePreviewImpl simTextFilePreview;
public MarkdownFilePreviewImpl(SimTextFilePreviewImpl simTextFilePreview) {
this.simTextFilePreview = simTextFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
return MARKDOWN_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,164 @@
package cn.keking.service.impl;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FileHandlerService;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.ObjectUtils;
import java.io.File;
/**
* @author : kl
* @authorboke : kailing.pub
* @create : 2018-03-25 上午11:58
* @description:
**/
@Service
public class MediaFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
private static final String mp4 = "mp4";
public MediaFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName = fileAttribute.getName();
String suffix = fileAttribute.getSuffix();
String cacheName = fileAttribute.getCacheName();
String outFilePath = fileAttribute.getOutFilePath();
boolean forceUpdatedCache = fileAttribute.forceUpdatedCache();
FileType type = fileAttribute.getType();
String[] mediaTypesConvert = FileType.MEDIA_CONVERT_TYPES; //获取支持的转换格式
boolean mediaTypes = false;
for (String temp : mediaTypesConvert) {
if (suffix.equals(temp)) {
mediaTypes = true;
break;
}
}
if (!url.toLowerCase().startsWith("http") || checkNeedConvert(mediaTypes)) { //不是http协议的 // 开启转换方式并是支持转换格式的
if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(cacheName) || !ConfigConstants.isCacheEnabled()) { //查询是否开启缓存
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
String filePath = response.getContent();
String convertedUrl = null;
try {
if (mediaTypes) {
convertedUrl = convertToMp4(filePath, outFilePath, fileAttribute);
} else {
convertedUrl = outFilePath; //其他协议的 不需要转换方式的文件 直接输出
}
} catch (Exception e) {
e.printStackTrace();
}
if (convertedUrl == null) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "视频转换异常,请联系管理员");
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileHandlerService.addConvertedFile(cacheName, fileHandlerService.getRelativePath(outFilePath));
}
model.addAttribute("mediaUrl", fileHandlerService.getRelativePath(outFilePath));
} else {
model.addAttribute("mediaUrl", fileHandlerService.listConvertedFiles().get(cacheName));
}
return MEDIA_FILE_PREVIEW_PAGE;
}
if (type.equals(FileType.MEDIA)) { // 支持输出 只限默认格式
model.addAttribute("mediaUrl", url);
return MEDIA_FILE_PREVIEW_PAGE;
}
return otherFilePreview.notSupportedFile(model, fileAttribute, "系统还不支持该格式文件的在线预览");
}
/**
* 检查视频文件转换是否已开启,以及当前文件是否需要转换
*
* @return
*/
private boolean checkNeedConvert(boolean mediaTypes) {
//1.检查开关是否开启
if ("true".equals(ConfigConstants.getMediaConvertDisable())) {
return mediaTypes;
}
return false;
}
private static String convertToMp4(String filePath, String outFilePath, FileAttribute fileAttribute) throws Exception {
FFmpegFrameGrabber frameGrabber = FFmpegFrameGrabber.createDefault(filePath);
Frame captured_frame;
FFmpegFrameRecorder recorder = null;
try {
File desFile = new File(outFilePath);
//判断一下防止重复转换
if (desFile.exists()) {
return outFilePath;
}
if (fileAttribute.isCompressFile()) { //判断 是压缩包的创建新的目录
int index = outFilePath.lastIndexOf("/"); //截取最后一个斜杠的前面的内容
String folder = outFilePath.substring(0, index);
File path = new File(folder);
//目录不存在 创建新的目录
if (!path.exists()) {
path.mkdirs();
}
}
frameGrabber.start();
recorder = new FFmpegFrameRecorder(outFilePath, frameGrabber.getImageWidth(), frameGrabber.getImageHeight(), frameGrabber.getAudioChannels());
// recorder.setImageHeight(640);
// recorder.setImageWidth(480);
recorder.setFormat(mp4);
recorder.setFrameRate(frameGrabber.getFrameRate());
recorder.setSampleRate(frameGrabber.getSampleRate());
//视频编码属性配置 H.264 H.265 MPEG
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
//设置视频比特率,单位:b
recorder.setVideoBitrate(frameGrabber.getVideoBitrate());
recorder.setAspectRatio(frameGrabber.getAspectRatio());
// 设置音频通用编码格式
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
//设置音频比特率,单位:b (比特率越高,清晰度/音质越好,当然文件也就越大 128000 = 182kb)
recorder.setAudioBitrate(frameGrabber.getAudioBitrate());
recorder.setAudioOptions(frameGrabber.getAudioOptions());
recorder.setAudioChannels(frameGrabber.getAudioChannels());
recorder.start();
while (true) {
captured_frame = frameGrabber.grabFrame();
if (captured_frame == null) {
System.out.println("转码完成:" + filePath);
break;
}
recorder.record(captured_frame);
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (recorder != null) { //关闭
recorder.stop();
recorder.close();
}
frameGrabber.stop();
frameGrabber.close();
}
return outFilePath;
}
}

View File

@@ -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;
/**
* ofd 图片文件处理
* @author kl (http://kailing.pub)
* @since 2021/2/8
*/
@Service
public class OfdFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public OfdFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return OFD_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,148 @@
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.service.OfficeToPdfService;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.OfficeUtils;
import cn.keking.utils.WebUtils;
import cn.keking.web.filter.BaseUrlFilter;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.EncryptedDocumentException;
import org.jodconverter.core.office.OfficeException;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.List;
/**
* Created by kl on 2018/1/17.
* Content :处理office文件
*/
@Service
public class OfficeFilePreviewImpl implements FilePreview {
public static final String OFFICE_PREVIEW_TYPE_IMAGE = "image";
public static final String OFFICE_PREVIEW_TYPE_ALL_IMAGES = "allImages";
private static final String OFFICE_PASSWORD_MSG = "password";
private final FileHandlerService fileHandlerService;
private final OfficeToPdfService officeToPdfService;
private final OtherFilePreviewImpl otherFilePreview;
public OfficeFilePreviewImpl(FileHandlerService fileHandlerService, OfficeToPdfService officeToPdfService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.officeToPdfService = officeToPdfService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 预览Type参数传了就取参数的没传取系统默认
String officePreviewType = fileAttribute.getOfficePreviewType();
boolean userToken = fileAttribute.getUsePasswordCache();
String baseUrl = BaseUrlFilter.getBaseUrl();
String suffix = fileAttribute.getSuffix(); //获取文件后缀
String fileName = fileAttribute.getName(); //获取文件原始名称
String filePassword = fileAttribute.getFilePassword(); //获取密码
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache(); //是否启用强制更新命令
boolean isHtmlView = fileAttribute.isHtmlView(); //xlsx 转换成html
String cacheName = fileAttribute.getCacheName(); //转换后的文件名
String outFilePath = fileAttribute.getOutFilePath(); //转换后生成文件的路径
if (!officePreviewType.equalsIgnoreCase("html")) {
if (ConfigConstants.getOfficeTypeWeb() .equalsIgnoreCase("web")) {
if (suffix.equalsIgnoreCase("xlsx")) {
model.addAttribute("pdfUrl", KkFileUtils.htmlEscape(url)); //特殊符号处理
return XLSX_FILE_PREVIEW_PAGE;
}
if (suffix.equalsIgnoreCase("csv")) {
model.addAttribute("csvUrl", KkFileUtils.htmlEscape(url));
return CSV_FILE_PREVIEW_PAGE;
}
}
}
if (forceUpdatedCache|| !fileHandlerService.listConvertedFiles().containsKey(cacheName) || !ConfigConstants.isCacheEnabled()) {
// 下载远程文件到本地,如果文件在本地已存在不会重复下载
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
String filePath = response.getContent();
boolean isPwdProtectedOffice = OfficeUtils.isPwdProtected(filePath); // 判断是否加密文件
if (isPwdProtectedOffice && !StringUtils.hasLength(filePassword)) {
// 加密文件需要密码
model.addAttribute("needFilePassword", true);
return EXEL_FILE_PREVIEW_PAGE;
} else {
if (StringUtils.hasText(outFilePath)) {
try {
officeToPdfService.openOfficeToPDF(filePath, outFilePath, fileAttribute);
} catch (OfficeException e) {
if (isPwdProtectedOffice && !OfficeUtils.isCompatible(filePath, filePassword)) {
// 加密文件密码错误,提示重新输入
model.addAttribute("needFilePassword", true);
model.addAttribute("filePasswordError", true);
return EXEL_FILE_PREVIEW_PAGE;
}
return otherFilePreview.notSupportedFile(model, fileAttribute, "抱歉,该文件版本不兼容,文件版本错误。");
}
if (isHtmlView) {
// 对转换后的文件进行操作(改变编码方式)
fileHandlerService.doActionConvertedFile(outFilePath);
}
//是否保留OFFICE源文件
if (!fileAttribute.isCompressFile() && ConfigConstants.getDeleteSourceFile()) {
KkFileUtils.deleteFileByPath(filePath);
}
if (userToken || !isPwdProtectedOffice) {
// 加入缓存
fileHandlerService.addConvertedFile(cacheName, fileHandlerService.getRelativePath(outFilePath));
}
}
}
}
if (!isHtmlView && baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) {
return getPreviewType(model, fileAttribute, officePreviewType, cacheName, outFilePath, fileHandlerService, OFFICE_PREVIEW_TYPE_IMAGE, otherFilePreview);
}
model.addAttribute("pdfUrl", WebUtils.encodeFileName(cacheName)); //输出转义文件名 方便url识别
return isHtmlView ? EXEL_FILE_PREVIEW_PAGE : PDF_FILE_PREVIEW_PAGE;
}
static String getPreviewType(Model model, FileAttribute fileAttribute, String officePreviewType, String pdfName, String outFilePath, FileHandlerService fileHandlerService, String officePreviewTypeImage, OtherFilePreviewImpl otherFilePreview) {
String suffix = fileAttribute.getSuffix();
boolean isPPT = suffix.equalsIgnoreCase("ppt") || suffix.equalsIgnoreCase("pptx");
List<String> imageUrls = null;
try {
imageUrls = fileHandlerService.pdf2jpg(outFilePath,outFilePath, pdfName, fileAttribute);
} catch (Exception e) {
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
for (Throwable throwable : throwableArray) {
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
if (e.getMessage().toLowerCase().contains(OFFICE_PASSWORD_MSG)) {
model.addAttribute("needFilePassword", true);
return EXEL_FILE_PREVIEW_PAGE;
}
}
}
}
if (imageUrls == null || imageUrls.size() < 1) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "office转图片异常请联系管理员");
}
model.addAttribute("imgUrls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (officePreviewTypeImage.equals(officePreviewType)) {
// PPT 图片模式使用专用预览页面
return (isPPT ? PPT_FILE_PREVIEW_PAGE : OFFICE_PICTURE_FILE_PREVIEW_PAGE);
} else {
return PICTURE_FILE_PREVIEW_PAGE;
}
}
}

View File

@@ -0,0 +1,26 @@
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;
/**
* Created by kl on 2018/1/17.
* Content :图片文件处理
*/
@Service
public class Online3DFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public Online3DFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return ONLINE3D_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,52 @@
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;
/**
* Created by kl on 2018/1/17.
* Content :其他文件
*/
@Service
public class OtherFilePreviewImpl implements FilePreview {
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
return this.notSupportedFile(model, fileAttribute, "系统还不支持该格式文件的在线预览");
}
/**
* 通用的预览失败,导向到不支持的文件响应页面
*
* @return 页面
*/
public String notSupportedFile(Model model, FileAttribute fileAttribute, String errMsg) {
return this.notSupportedFile(model, fileAttribute.getSuffix(), errMsg);
}
/**
* 通用的预览失败,导向到不支持的文件响应页面
*
* @return 页面
*/
public String notSupportedFile(Model model, String errMsg) {
return this.notSupportedFile(model, "未知", errMsg);
}
/**
* 通用的预览失败,导向到不支持的文件响应页面
*
* @return 页面
*/
public String notSupportedFile(Model model, String fileType, String errMsg) {
model.addAttribute("fileType", KkFileUtils.htmlEscape(fileType));
model.addAttribute("msg", KkFileUtils.htmlEscape(errMsg));
return NOT_SUPPORTED_FILE_PAGE;
}
}

View File

@@ -0,0 +1,99 @@
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.WebUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.EncryptedDocumentException;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.io.IOException;
import java.util.List;
/**
* Created by kl on 2018/1/17.
* Content :处理pdf文件
*/
@Service
public class PdfFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
private static final String PDF_PASSWORD_MSG = "password";
public PdfFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String pdfName = fileAttribute.getName(); //获取原始文件名
String officePreviewType = fileAttribute.getOfficePreviewType(); //转换类型
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache(); //是否启用强制更新命令
String outFilePath = fileAttribute.getOutFilePath(); //生成的文件路径
String originFilePath = fileAttribute.getOriginFilePath(); //原始文件路径
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) {
//当文件不存在时,就去下载
if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, pdfName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
originFilePath = response.getContent();
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(originFilePath));
}
}
List<String> imageUrls;
try {
imageUrls = fileHandlerService.pdf2jpg(originFilePath,outFilePath, pdfName, fileAttribute);
} catch (Exception e) {
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
for (Throwable throwable : throwableArray) {
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
if (e.getMessage().toLowerCase().contains(PDF_PASSWORD_MSG)) {
model.addAttribute("needFilePassword", true);
return EXEL_FILE_PREVIEW_PAGE;
}
}
}
return otherFilePreview.notSupportedFile(model, fileAttribute, "pdf转图片异常请联系管理员");
}
if (imageUrls == null || imageUrls.size() < 1) {
return otherFilePreview.notSupportedFile(model, fileAttribute, "pdf转图片异常请联系管理员");
}
model.addAttribute("imgUrls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) {
return OFFICE_PICTURE_FILE_PREVIEW_PAGE;
} else {
return PICTURE_FILE_PREVIEW_PAGE;
}
} else {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
if (!fileHandlerService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, pdfName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
model.addAttribute("pdfUrl", fileHandlerService.getRelativePath(response.getContent()));
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileHandlerService.addConvertedFile(pdfName, fileHandlerService.getRelativePath(outFilePath));
}
} else {
model.addAttribute("pdfUrl", WebUtils.encodeFileName(pdfName));
}
} else {
model.addAttribute("pdfUrl", url);
}
}
return PDF_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,42 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
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;
/**
* Created by kl on 2018/1/17.
* Content :图片文件处理
*/
@Service
public class PictureFilePreviewImpl extends CommonPreviewImpl {
private final FileHandlerService fileHandlerService;
public PictureFilePreviewImpl(FileHandlerService fileHandlerService, OtherFilePreviewImpl otherFilePreview) {
super(fileHandlerService, otherFilePreview);
this.fileHandlerService = fileHandlerService;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
url= KkFileUtils.htmlEscape(url);
List<String> imgUrls = new ArrayList<>();
imgUrls.add(url);
String compressFileKey = fileAttribute.getCompressFileKey();
List<String> zipImgUrls = fileHandlerService.getImgCache(compressFileKey);
if (!CollectionUtils.isEmpty(zipImgUrls)) {
imgUrls.addAll(zipImgUrls);
}
// 不是http开头浏览器不能直接访问需下载到本地
super.filePreviewHandle(url, model, fileAttribute);
model.addAttribute("imgUrls", imgUrls);
return PICTURE_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,88 @@
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;
import cn.keking.utils.KkFileUtils;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.web.util.HtmlUtils;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* Created by kl on 2018/1/17.
* Content :处理文本文件
*/
@Service
public class SimTextFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public SimTextFilePreviewImpl(FileHandlerService fileHandlerService,OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName = fileAttribute.getName();
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache();
String filePath = fileAttribute.getOriginFilePath();
if (forceUpdatedCache || !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,fileName));
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 {
fileData = HtmlUtils.htmlEscape(textData(filePath,fileName));
} catch (IOException e) {
e.printStackTrace();
}
model.addAttribute("textData", Base64.encodeBase64String(fileData.getBytes()));
return TXT_FILE_PREVIEW_PAGE;
}
private String textData(String filePath,String fileName) throws IOException {
File file = new File(filePath);
if (KkFileUtils.isIllegalFileName(fileName)) {
return null;
}
if (!file.exists() || file.length() == 0) {
return "";
} else {
String charset = EncodingDetects.getJavaEncode(filePath);
if ("ASCII".equals(charset)) {
charset = StandardCharsets.US_ASCII.name();
}
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), charset));
StringBuilder result = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
result.append(line).append("\r\n");
}
br.close();
return result.toString();
}
}
}

View File

@@ -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 CommonPreviewImpl commonPreview;
public SvgFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return SVG_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,127 @@
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.ConvertPicUtil;
import cn.keking.utils.DownloadUtils;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.WebUtils;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.util.List;
/**
* tiff 图片文件处理
*
* @author kl (http://kailing.pub)
* @since 2021/2/8
*/
@Service
public class TiffFilePreviewImpl implements FilePreview {
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
public TiffFilePreviewImpl(FileHandlerService fileHandlerService,OtherFilePreviewImpl otherFilePreview) {
this.fileHandlerService = fileHandlerService;
this.otherFilePreview = otherFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName = fileAttribute.getName();
String tifPreviewType = ConfigConstants.getTifPreviewType();
String cacheName = fileAttribute.getCacheName();
String outFilePath = fileAttribute.getOutFilePath();
boolean forceUpdatedCache=fileAttribute.forceUpdatedCache();
if ("jpg".equalsIgnoreCase(tifPreviewType) || "pdf".equalsIgnoreCase(tifPreviewType)) {
if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(cacheName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
String filePath = response.getContent();
if ("pdf".equalsIgnoreCase(tifPreviewType)) {
try {
ConvertPicUtil.convertJpg2Pdf(filePath, outFilePath);
} catch (Exception e) {
if (e.getMessage().contains("Bad endianness tag (not 0x4949 or 0x4d4d)") ) {
model.addAttribute("imgUrls", url);
model.addAttribute("currentUrl", url);
return PICTURE_FILE_PREVIEW_PAGE;
}else {
return otherFilePreview.notSupportedFile(model, fileAttribute, "TIF转pdf异常请联系系统管理员!" );
}
}
//是否保留TIFF源文件
if (!fileAttribute.isCompressFile() && ConfigConstants.getDeleteSourceFile()) {
// KkFileUtils.deleteFileByPath(filePath);
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileHandlerService.addConvertedFile(cacheName, fileHandlerService.getRelativePath(outFilePath));
}
model.addAttribute("pdfUrl", WebUtils.encodeFileName(cacheName));
return PDF_FILE_PREVIEW_PAGE;
}else {
// 将tif转换为jpg返回转换后的文件路径、文件名的list
List<String> listPic2Jpg;
try {
listPic2Jpg = ConvertPicUtil.convertTif2Jpg(filePath, outFilePath,forceUpdatedCache);
} catch (Exception e) {
if (e.getMessage().contains("Bad endianness tag (not 0x4949 or 0x4d4d)") ) {
model.addAttribute("imgUrls", url);
model.addAttribute("currentUrl", url);
return PICTURE_FILE_PREVIEW_PAGE;
}else {
return otherFilePreview.notSupportedFile(model, fileAttribute, "TIF转JPG异常请联系系统管理员!" );
}
}
//是否保留源文件,转换失败保留源文件,转换成功删除源文件
if(!fileAttribute.isCompressFile() && ConfigConstants.getDeleteSourceFile()) {
KkFileUtils.deleteFileByPath(filePath);
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileHandlerService.putImgCache(cacheName, listPic2Jpg);
fileHandlerService.addConvertedFile(cacheName, fileHandlerService.getRelativePath(outFilePath));
}
model.addAttribute("imgUrls", listPic2Jpg);
model.addAttribute("currentUrl", listPic2Jpg.get(0));
return PICTURE_FILE_PREVIEW_PAGE;
}
}
if ("pdf".equalsIgnoreCase(tifPreviewType)) {
model.addAttribute("pdfUrl", WebUtils.encodeFileName(cacheName));
return PDF_FILE_PREVIEW_PAGE;
}
else if ("jpg".equalsIgnoreCase(tifPreviewType)) {
model.addAttribute("imgUrls", fileHandlerService.getImgCache(cacheName));
model.addAttribute("currentUrl", fileHandlerService.getImgCache(cacheName).get(0));
return PICTURE_FILE_PREVIEW_PAGE;
}
}
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
if (forceUpdatedCache || !fileHandlerService.listConvertedFiles().containsKey(fileName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = DownloadUtils.downLoad(fileAttribute, fileName);
if (response.isFailure()) {
return otherFilePreview.notSupportedFile(model, fileAttribute, response.getMsg());
}
model.addAttribute("currentUrl", fileHandlerService.getRelativePath(response.getContent()));
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
fileHandlerService.addConvertedFile(fileName, fileHandlerService.getRelativePath(outFilePath));
}
} else {
model.addAttribute("currentUrl", WebUtils.encodeFileName(fileName));
}
return TIFF_FILE_PREVIEW_PAGE;
}
model.addAttribute("currentUrl", url);
return TIFF_FILE_PREVIEW_PAGE;
}
}

View File

@@ -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 XmindFilePreviewImpl implements FilePreview {
private final CommonPreviewImpl commonPreview;
public XmindFilePreviewImpl(CommonPreviewImpl commonPreview) {
this.commonPreview = commonPreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
commonPreview.filePreviewHandle(url,model,fileAttribute);
return XMIND_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,26 @@
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;
/**
* @author kl (http://kailing.pub)
* @since 2020/12/25
*/
@Service
public class XmlFilePreviewImpl implements FilePreview {
private final SimTextFilePreviewImpl simTextFilePreview;
public XmlFilePreviewImpl(SimTextFilePreviewImpl simTextFilePreview) {
this.simTextFilePreview = simTextFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
return XML_FILE_PREVIEW_PAGE;
}
}

View File

@@ -0,0 +1,74 @@
package cn.keking.utils;
import org.springframework.util.Assert;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
public class CaptchaUtil {
public static final String CAPTCHA_CODE = "captchaCode";
public static final String CAPTCHA_GENERATE_TIME = "captchaTime";
private static final int WIDTH = 100;// 定义图片的width
private static final int HEIGHT = 30;// 定义图片的height
private static final int CODE_LENGTH = 4;// 定义图片上显示验证码的个数
private static final int FONT_HEIGHT = 28;
private static final char[] CODE_SEQUENCE = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', '2', '3', '4', '5', '6', '7', '8', '9'};
/**
* 指定验证码、生成验证码图片。
* @param captchaCode 验证码
* @return 验证码图片
*/
public static BufferedImage generateCaptchaPic(final String captchaCode) {
Assert.notNull(captchaCode, "captchaCode must not be null");
// 定义图像buffer
BufferedImage buffImg = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics gd = buffImg.getGraphics();
Random random = new Random();
// 将图像填充为白色
gd.setColor(Color.WHITE);
gd.fillRect(0, 0, WIDTH, HEIGHT);
Font font = new Font("Times New Roman", Font.BOLD, FONT_HEIGHT);
gd.setFont(font);
// 画边框。
gd.setColor(Color.BLACK);
gd.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);
// 随机产生40条干扰线使图象中的认证码不易被其它程序探测到。
gd.setColor(Color.BLACK);
for (int i = 0; i < 30; i++) {
int x = random.nextInt(WIDTH);
int y = random.nextInt(HEIGHT);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
gd.drawLine(x, y, x + xl, y + yl);
}
// randomCode用于保存随机产生的验证码以便用户登录后进行验证。
int red, green, blue;
// 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
// 用随机产生的颜色将验证码绘制到图像中。
gd.setColor(new Color(red, green, blue));
gd.drawString(captchaCode, 18, 27);
return buffImg;
}
/**
* 生成随机字符串。
* @return 字符串
*/
public static String generateCaptchaCode() {
Random random = new Random();
StringBuilder randomCode = new StringBuilder();
for (int i = 0; i < CODE_LENGTH; i++) {
randomCode.append(CODE_SEQUENCE[random.nextInt(52)]);
}
return randomCode.toString();
}
}

View File

@@ -0,0 +1,99 @@
package cn.keking.utils;
import java.io.File;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
/**
* @author : kl
**/
public class ConfigUtils {
private static final String MAIN_DIRECTORY_NAME = "tool-tech-file-view";
public static String getHomePath() {
String userDir = System.getenv("KKFILEVIEW_BIN_FOLDER");
if (userDir == null) {
userDir = System.getProperty("user.dir");
}
if (userDir.endsWith("bin")) {
userDir = userDir.substring(0, userDir.length() - 4);
} else {
String separator = File.separator;
if (userDir.endsWith(MAIN_DIRECTORY_NAME)) {
userDir = userDir + separator + "src" + separator + "main";
} else {
userDir = userDir + separator + MAIN_DIRECTORY_NAME + separator + "src" + separator + "main";
}
}
return userDir;
}
// 获取环境变量,如果找不到则返回默认值
@SuppressWarnings("SameParameterValue")
private static String getEnvOrDefault(String key, String def) {
String value = System.getenv(key);
return value == null ? def : value;
}
// 返回参数列表中第一个真实存在的路径,或者 null
private static String firstExists(File... paths) {
for (File path : paths) {
if (path.exists()) {
return path.getAbsolutePath();
}
}
return null;
}
public static String getUserDir() {
String userDir = System.getProperty("user.dir");
String binFolder = getEnvOrDefault("KKFILEVIEW_BIN_FOLDER", userDir);
File pluginPath = new File(binFolder);
// 如果指定了 bin 或其父目录,则返回父目录
if (new File(pluginPath, "bin").exists()) {
return pluginPath.getAbsolutePath();
} else if (pluginPath.exists() && pluginPath.getName().equals("bin")) {
return pluginPath.getParentFile().getAbsolutePath();
} else {
return firstExists(new File(pluginPath, MAIN_DIRECTORY_NAME),
new File(pluginPath.getParentFile(), MAIN_DIRECTORY_NAME));
}
}
public static String getCustomizedConfigPath() {
String homePath = getHomePath();
String separator = java.io.File.separator;
return homePath + separator + "config" + separator + "application.properties";
}
public synchronized static void restorePropertiesFromEnvFormat(Properties properties) {
Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> entry = iterator.next();
String key = entry.getKey().toString();
String value = entry.getValue().toString();
if (value.trim().startsWith("${") && value.trim().endsWith("}")) {
int beginIndex = value.indexOf(":");
if (beginIndex < 0) {
beginIndex = value.length() - 1;
}
int endIndex = value.length() - 1;
String envKey = value.substring(2, beginIndex);
String envValue = System.getenv(envKey);
if (envValue == null || "".equals(envValue.trim())) {
value = value.substring(beginIndex + 1, endIndex);
} else {
value = envValue;
}
properties.setProperty(key, value);
}
}
}
}

View File

@@ -0,0 +1,138 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.web.filter.BaseUrlFilter;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.io.FileChannelRandomAccessSource;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.text.pdf.RandomAccessFileOrArray;
import com.itextpdf.text.pdf.codec.TiffImage;
import com.sun.media.jai.codec.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;
public class ConvertPicUtil {
private static final int FIT_WIDTH = 500;
private static final int FIT_HEIGHT = 900;
private final static Logger logger = LoggerFactory.getLogger(ConvertPicUtil.class);
private final static String fileDir = ConfigConstants.getFileDir();
/**
* Tif 转 JPG。
*
* @param strInputFile 输入文件的路径和文件名
* @param strOutputFile 输出文件的路径和文件名
* @return boolean 是否转换成功
*/
public static List<String> convertTif2Jpg(String strInputFile, String strOutputFile, boolean forceUpdatedCache) throws Exception {
List<String> listImageFiles = new ArrayList<>();
String baseUrl = BaseUrlFilter.getBaseUrl();
if (!new File(strInputFile).exists()) {
logger.info("找不到文件【" + strInputFile + "");
return null;
}
strOutputFile = strOutputFile.replaceAll(".jpg", "");
FileSeekableStream fileSeekStream = null;
try {
JPEGEncodeParam jpegEncodeParam = new JPEGEncodeParam();
TIFFEncodeParam tiffEncodeParam = new TIFFEncodeParam();
tiffEncodeParam.setCompression(TIFFEncodeParam.COMPRESSION_GROUP4);
tiffEncodeParam.setLittleEndian(false);
fileSeekStream = new FileSeekableStream(strInputFile);
ImageDecoder imageDecoder = ImageCodec.createImageDecoder("TIFF", fileSeekStream, null);
int intTifCount = imageDecoder.getNumPages();
// logger.info("该tif文件共有【" + intTifCount + "】页");
// 处理目标文件夹,如果不存在则自动创建
File fileJpgPath = new File(strOutputFile);
if (!fileJpgPath.exists() && !fileJpgPath.mkdirs()) {
logger.error("{} 创建失败", strOutputFile);
}
// 循环处理每页tif文件转换为jpg
for (int i = 0; i < intTifCount; i++) {
String strJpg= strOutputFile + "/" + i + ".jpg";
File fileJpg = new File(strJpg);
// 如果文件不存在,则生成
if (forceUpdatedCache|| !fileJpg.exists()) {
RenderedImage renderedImage = imageDecoder.decodeAsRenderedImage(i);
ParameterBlock pb = new ParameterBlock();
pb.addSource(renderedImage);
pb.add(fileJpg.toString());
pb.add("JPEG");
pb.add(jpegEncodeParam);
RenderedOp renderedOp = JAI.create("filestore", pb);
renderedOp.dispose();
// logger.info("每页分别保存至: " + fileJpg.getCanonicalPath());
}
strJpg = baseUrl+strJpg.replace(fileDir, "");
listImageFiles.add(strJpg);
}
} catch (IOException e) {
if (!e.getMessage().contains("Bad endianness tag (not 0x4949 or 0x4d4d)") ) {
logger.error("TIF转JPG异常文件路径" + strInputFile, e);
}
throw new Exception(e);
} finally {
if (fileSeekStream != null) {
fileSeekStream.close();
}
}
return listImageFiles;
}
/**
* 将Jpg图片转换为Pdf文件
*
* @param strJpgFile 输入的jpg的路径和文件名
* @param strPdfFile 输出的pdf的路径和文件名
*/
public static String convertJpg2Pdf(String strJpgFile, String strPdfFile) throws Exception {
Document document = new Document();
RandomAccessFileOrArray rafa = null;
FileOutputStream outputStream = null;
try {
RandomAccessFile aFile = new RandomAccessFile(strJpgFile, "r");
FileChannel inChannel = aFile.getChannel();
FileChannelRandomAccessSource fcra = new FileChannelRandomAccessSource(inChannel);
rafa = new RandomAccessFileOrArray(fcra);
int pages = TiffImage.getNumberOfPages(rafa);
outputStream = new FileOutputStream(strPdfFile);
PdfWriter.getInstance(document, outputStream);
document.open();
Image image;
for (int i = 1; i <= pages; i++) {
image = TiffImage.getTiffImage(rafa, i);
image.scaleToFit(FIT_WIDTH, FIT_HEIGHT);
document.add(image);
}
} catch (IOException e) {
if (!e.getMessage().contains("Bad endianness tag (not 0x4949 or 0x4d4d)") ) {
logger.error("TIF转JPG异常文件路径" + strPdfFile, e);
}
throw new Exception(e);
} finally {
if (document != null) {
document.close();
}
if (rafa != null) {
rafa.close();
}
if (outputStream != null) {
outputStream.close();
}
}
return strPdfFile;
}
}

View File

@@ -0,0 +1,26 @@
package cn.keking.utils;
import java.time.Instant;
/**
* @author kl (http://kailing.pub)
* @since 2023/8/11
*/
public class DateUtils {
/**
* 获取当前时间的秒级时间戳
* @return
*/
public static long getCurrentSecond() {
return Instant.now().getEpochSecond();
}
/**
* 计算当前时间与指定时间的秒级时间戳差值
* @param datetime 指定时间
* @return 差值
*/
public static long calculateCurrentTimeDifference(long datetime) {
return getCurrentSecond() - datetime;
}
}

View File

@@ -0,0 +1,168 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.mola.galimatias.GalimatiasParseException;
import org.apache.commons.io.FileUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import static cn.keking.utils.KkFileUtils.isFtpUrl;
import static cn.keking.utils.KkFileUtils.isHttpUrl;
/**
* @author yudian-it
*/
public class DownloadUtils {
private final static Logger logger = LoggerFactory.getLogger(DownloadUtils.class);
private static final String fileDir = ConfigConstants.getFileDir();
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 RestTemplate restTemplate = new RestTemplate();
private static final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
private static final ObjectMapper mapper = new ObjectMapper();
/**
* @param fileAttribute fileAttribute
* @param fileName 文件名
* @return 本地文件绝对路径
*/
public static ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
// 忽略ssl证书
String urlStr = null;
try {
SslUtils.ignoreSsl();
urlStr = fileAttribute.getUrl().replaceAll("\\+", "%20").replaceAll(" ", "%20");
} catch (Exception e) {
logger.error("忽略SSL证书异常:", e);
}
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
String realPath = getRelFilePath(fileName, fileAttribute);
// 判断是否非法地址
if (KkFileUtils.isIllegalFileName(realPath)) {
response.setCode(1);
response.setContent(null);
response.setMsg("下载失败:文件名不合法!" + urlStr);
return response;
}
if (!KkFileUtils.isAllowedUpload(realPath)) {
response.setCode(1);
response.setContent(null);
response.setMsg("下载失败:不支持的类型!" + urlStr);
return response;
}
if (fileAttribute.isCompressFile()) { //压缩包文件 直接赋予路径 不予下载
response.setContent(fileDir + fileName);
response.setMsg(fileName);
return response;
}
// 如果文件是否已经存在、且不强制更新,则直接返回文件路径
if (KkFileUtils.isExist(realPath) && !fileAttribute.forceUpdatedCache()) {
response.setContent(realPath);
response.setMsg(fileName);
return response;
}
try {
URL url = WebUtils.normalizedURL(urlStr);
if (!fileAttribute.getSkipDownLoad()) {
if (isHttpUrl(url)) {
File realFile = new File(realPath);
factory.setConnectionRequestTimeout(2000); //设置超时时间
factory.setConnectTimeout(10000);
factory.setReadTimeout(72000);
HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new DefaultRedirectStrategy()).build();
factory.setHttpClient(httpClient); //加入重定向方法
restTemplate.setRequestFactory(factory);
RequestCallback requestCallback = request -> {
request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
String proxyAuthorization = fileAttribute.getKkProxyAuthorization();
if(StringUtils.hasText(proxyAuthorization)){
Map<String,String> proxyAuthorizationMap = mapper.readValue(proxyAuthorization, Map.class);
proxyAuthorizationMap.forEach((key, value) -> request.getHeaders().set(key, value));
}
};
try {
restTemplate.execute(url.toURI(), HttpMethod.GET, requestCallback, fileResponse -> {
FileUtils.copyToFile(fileResponse.getBody(), realFile);
return null;
});
} catch (Exception e) {
response.setCode(1);
response.setContent(null);
response.setMsg("下载失败:" + e);
return response;
}
} else if (isFtpUrl(url)) {
String ftpUsername = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = WebUtils.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else {
response.setCode(1);
response.setMsg("url不能识别url" + urlStr);
}
}
response.setContent(realPath);
response.setMsg(fileName);
return response;
} catch (IOException | GalimatiasParseException e) {
logger.error("文件下载失败url{}", urlStr);
response.setCode(1);
response.setContent(null);
if (e instanceof FileNotFoundException) {
response.setMsg("文件不存在!!!");
} else {
response.setMsg(e.getMessage());
}
return response;
}
}
/**
* 获取真实文件绝对路径
*
* @param fileName 文件名
* @return 文件路径
*/
private static String getRelFilePath(String fileName, FileAttribute fileAttribute) {
String type = fileAttribute.getSuffix();
if (null == fileName) {
UUID uuid = UUID.randomUUID();
fileName = uuid + "." + type;
} else { // 文件后缀不一致时以type为准(针对simText【将类txt文件转为txt】)
fileName = fileName.replace(fileName.substring(fileName.lastIndexOf(".") + 1), type);
}
String realPath = fileDir + fileName;
File dirFile = new File(fileDir);
if (!dirFile.exists() && !dirFile.mkdirs()) {
logger.error("创建目录【{}】失败,可能是权限不够,请检查", fileDir);
}
return realPath;
}
}

View File

@@ -0,0 +1,51 @@
package cn.keking.utils;
import org.mozilla.universalchardet.UniversalDetector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.file.Files;
/**
* @author asiawu
* @date 2023/07/20 17:26
* @description: 自动获取文件的编码
*/
public class EncodingDetects {
private static final int DEFAULT_LENGTH = 4096;
private static final int LIMIT = 50;
private static final Logger logger = LoggerFactory.getLogger(EncodingDetects.class);
public static String getJavaEncode(String filePath) {
return getJavaEncode(new File(filePath));
}
public static String getJavaEncode(File file) {
int len = Math.min(DEFAULT_LENGTH, (int) file.length());
byte[] content = new byte[len];
try (InputStream fis = Files.newInputStream(file.toPath())) {
fis.read(content, 0, len);
} catch (IOException e) {
logger.error("文件读取失败:{}", file.getPath());
}
return getJavaEncode(content);
}
public static String getJavaEncode(byte[] content) {
if (content != null && content.length <= LIMIT) {
return SimpleEncodingDetects.getJavaEncode(content);
}
UniversalDetector detector = new UniversalDetector(null);
detector.handleData(content, 0, content.length);
detector.dataEnd();
String charsetName = detector.getDetectedCharset();
if (charsetName == null) {
charsetName = Charset.defaultCharset().name();
}
return charsetName;
}
}

View File

@@ -0,0 +1,28 @@
package cn.keking.utils;
public class FileHeaderRar {
private String fileNameW;
private Boolean isDirectory;
public FileHeaderRar(String fileNameW, Boolean isDirectory) {
this.fileNameW = fileNameW;
this.isDirectory = isDirectory;
}
public String getFileNameW() {
return fileNameW;
}
public void setFileNameW(String fileNameW) {
this.fileNameW = fileNameW;
}
public Boolean getDirectory() {
return isDirectory;
}
public void setDirectory(Boolean directory) {
isDirectory = directory;
}
}

View File

@@ -0,0 +1,59 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* @auther: chenjh
* @since: 2019/6/18 14:36
*/
public class FtpUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(FtpUtils.class);
public static FTPClient connect(String host, int port, String username, String password, String controlEncoding) throws IOException {
FTPClient ftpClient = new FTPClient();
ftpClient.connect(host, port);
if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
ftpClient.login(username, password);
}
int reply = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
ftpClient.disconnect();
}
ftpClient.setControlEncoding(controlEncoding);
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
return ftpClient;
}
public static void download(String ftpUrl, String localFilePath, String ftpUsername, String ftpPassword, String ftpControlEncoding) throws IOException {
String username = StringUtils.isEmpty(ftpUsername) ? ConfigConstants.getFtpUsername() : ftpUsername;
String password = StringUtils.isEmpty(ftpPassword) ? ConfigConstants.getFtpPassword() : ftpPassword;
String controlEncoding = StringUtils.isEmpty(ftpControlEncoding) ? ConfigConstants.getFtpControlEncoding() : ftpControlEncoding;
URL url = new URL(ftpUrl);
String host = url.getHost();
int port = (url.getPort() == -1) ? url.getDefaultPort() : url.getPort();
String remoteFilePath = url.getPath();
LOGGER.debug("FTP connection url:{}, username:{}, password:{}, controlEncoding:{}, localFilePath:{}", ftpUrl, username, password, controlEncoding, localFilePath);
FTPClient ftpClient = connect(host, port, username, password, controlEncoding);
OutputStream outputStream = Files.newOutputStream(Paths.get(localFilePath));
ftpClient.enterLocalPassiveMode();
boolean downloadResult = ftpClient.retrieveFile(new String(remoteFilePath.getBytes(controlEncoding), StandardCharsets.ISO_8859_1), outputStream);
LOGGER.debug("FTP download result {}", downloadResult);
outputStream.flush();
outputStream.close();
ftpClient.logout();
ftpClient.disconnect();
}
}

View File

@@ -0,0 +1,212 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.HtmlUtils;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class KkFileUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(KkFileUtils.class);
public static final String DEFAULT_FILE_ENCODING = "UTF-8";
private static final List<String> illegalFileStrList = new ArrayList<>();
static {
illegalFileStrList.add("../");
illegalFileStrList.add("./");
illegalFileStrList.add("..\\");
illegalFileStrList.add(".\\");
illegalFileStrList.add("\\..");
illegalFileStrList.add("\\.");
illegalFileStrList.add("..");
illegalFileStrList.add("...");
}
/**
* 检查文件名是否合规
*
* @param fileName 文件名
* @return 合规结果, true:不合规false:合规
*/
public static boolean isIllegalFileName(String fileName) {
for (String str : illegalFileStrList) {
if (fileName.contains(str)) {
return true;
}
}
return false;
}
/**
* 检查是否是数字
*
* @param str 文件名
* @return 合规结果, true:不合规false:合规
*/
public static boolean isInteger(String str) {
if (StringUtils.hasText(str)) {
boolean strResult = str.matches("-?[0-9]+.?[0-9]*");
return strResult;
}
return false;
}
/**
* 判断url是否是http资源
*
* @param url url
* @return 是否http
*/
public static boolean isHttpUrl(URL url) {
return url.getProtocol().toLowerCase().startsWith("file") || url.getProtocol().toLowerCase().startsWith("http");
}
/**
* 判断url是否是ftp资源
*
* @param url url
* @return 是否ftp
*/
public static boolean isFtpUrl(URL url) {
return "ftp".equalsIgnoreCase(url.getProtocol());
}
/**
* 删除单个文件
*
* @param fileName 要删除的文件的文件名
* @return 单个文件删除成功返回true否则返回false
*/
public static boolean deleteFileByName(String fileName) {
File file = new File(fileName);
// 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
if (file.exists() && file.isFile()) {
if (file.delete()) {
LOGGER.info("删除单个文件" + fileName + "成功!");
return true;
} else {
LOGGER.info("删除单个文件" + fileName + "失败!");
return false;
}
} else {
LOGGER.info("删除单个文件失败:" + fileName + "不存在!");
return false;
}
}
public static String htmlEscape(String input) {
if (StringUtils.hasText(input)) {
//input = input.replaceAll("\\{", "%7B").replaceAll("}", "%7D").replaceAll("\\\\", "%5C");
String htmlStr = HtmlUtils.htmlEscape(input, "UTF-8");
//& -> &amp;
return htmlStr.replace("&amp;", "&");
}
return input;
}
/**
* 通过文件名获取文件后缀
*
* @param fileName 文件名称
* @return 文件后缀
*/
public static String suffixFromFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
}
/**
* 根据文件路径删除文件
*
* @param filePath 绝对路径
*/
public static void deleteFileByPath(String filePath) {
File file = new File(filePath);
if (file.exists() && !file.delete()) {
LOGGER.warn("压缩包源文件删除失败:{}", filePath);
}
}
/**
* 删除目录及目录下的文件
*
* @param dir 要删除的目录的文件路径
* @return 目录删除成功返回true否则返回false
*/
public static boolean deleteDirectory(String dir) {
// 如果dir不以文件分隔符结尾自动添加文件分隔符
if (!dir.endsWith(File.separator)) {
dir = dir + File.separator;
}
File dirFile = new File(dir);
// 如果dir对应的文件不存在或者不是一个目录则退出
if ((!dirFile.exists()) || (!dirFile.isDirectory())) {
LOGGER.info("删除目录失败:" + dir + "不存在!");
return false;
}
boolean flag = true;
// 删除文件夹中的所有文件包括子目录
File[] files = dirFile.listFiles();
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
// 删除子文件
if (files[i].isFile()) {
flag = KkFileUtils.deleteFileByName(files[i].getAbsolutePath());
if (!flag) {
break;
}
} else if (files[i].isDirectory()) {
// 删除子目录
flag = KkFileUtils.deleteDirectory(files[i].getAbsolutePath());
if (!flag) {
break;
}
}
}
if (!dirFile.delete() || !flag) {
LOGGER.info("删除目录失败!");
return false;
}
return true;
}
/**
* 判断文件是否允许上传
*
* @param file 文件扩展名
* @return 是否允许上传
*/
public static boolean isAllowedUpload(String file) {
String fileType = suffixFromFileName(file);
for (String type : ConfigConstants.getProhibit()) {
if (type.equals(fileType)){
return false;
}
}
return !ObjectUtils.isEmpty(fileType);
}
/**
* 判断文件是否存在
*
* @param filePath 文件路径
* @return 是否存在 true:存在false:不存在
*/
public static boolean isExist(String filePath) {
File file = new File(filePath);
return file.exists();
}
}

View File

@@ -0,0 +1,112 @@
package cn.keking.utils;
import org.jodconverter.core.util.OSUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.stream.Stream;
/**
* @author chenjh
* @since 2022-12-15
*/
public class LocalOfficeUtils {
public static final String OFFICE_HOME_KEY = "office.home";
public static final String DEFAULT_OFFICE_HOME_VALUE = "default";
private static final String EXECUTABLE_DEFAULT = "program/soffice.bin";
private static final String EXECUTABLE_MAC = "program/soffice";
private static final String EXECUTABLE_MAC_41 = "MacOS/soffice";
private static final String EXECUTABLE_WINDOWS = "program/soffice.exe";
public static File getDefaultOfficeHome() {
Properties properties = new Properties();
String customizedConfigPath = ConfigUtils.getCustomizedConfigPath();
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader(customizedConfigPath));
properties.load(bufferedReader);
ConfigUtils.restorePropertiesFromEnvFormat(properties);
} catch (Exception ignored) {}
String officeHome = properties.getProperty(OFFICE_HOME_KEY);
if (officeHome != null && !DEFAULT_OFFICE_HOME_VALUE.equals(officeHome)) {
return new File(officeHome);
}
if (OSUtils.IS_OS_WINDOWS) {
String userDir = ConfigUtils.getUserDir();
// Try to find the most recent version of LibreOffice or OpenOffice,
// starting with the 64-bit version. %ProgramFiles(x86)% on 64-bit
// machines; %ProgramFiles% on 32-bit ones
final String programFiles64 = System.getenv("ProgramFiles");
final String programFiles32 = System.getenv("ProgramFiles(x86)");
return findOfficeHome(EXECUTABLE_WINDOWS,
userDir + File.separator + "LibreOfficePortable" + File.separator + "App" + File.separator + "libreoffice",
programFiles32 + File.separator + "LibreOffice",
programFiles64 + File.separator + "LibreOffice 7",
programFiles32 + File.separator + "LibreOffice 7",
programFiles64 + File.separator + "LibreOffice 6",
programFiles32 + File.separator + "LibreOffice 6",
programFiles64 + File.separator + "LibreOffice 5",
programFiles32 + File.separator + "LibreOffice 5",
programFiles64 + File.separator + "LibreOffice 4",
programFiles32 + File.separator + "LibreOffice 4",
programFiles32 + File.separator + "OpenOffice 4",
programFiles64 + File.separator + "LibreOffice 3",
programFiles32 + File.separator + "LibreOffice 3",
programFiles32 + File.separator + "OpenOffice.org 3");
} else if (OSUtils.IS_OS_MAC) {
File homeDir = findOfficeHome(EXECUTABLE_MAC_41,
"/Applications/LibreOffice.app/Contents",
"/Applications/OpenOffice.app/Contents",
"/Applications/OpenOffice.org.app/Contents");
if (homeDir == null) {
homeDir = findOfficeHome(EXECUTABLE_MAC,
"/Applications/LibreOffice.app/Contents",
"/Applications/OpenOffice.app/Contents",
"/Applications/OpenOffice.org.app/Contents");
}
return homeDir;
} else {
// Linux or other *nix variants
return findOfficeHome(EXECUTABLE_DEFAULT,
"/opt/libreoffice6.0",
"/opt/libreoffice6.1",
"/opt/libreoffice6.2",
"/opt/libreoffice6.3",
"/opt/libreoffice6.4",
"/opt/libreoffice7.0",
"/opt/libreoffice7.1",
"/opt/libreoffice7.2",
"/opt/libreoffice7.3",
"/opt/libreoffice7.4",
"/opt/libreoffice7.5",
"/opt/libreoffice7.6",
"/usr/lib64/libreoffice",
"/usr/lib/libreoffice",
"/usr/local/lib64/libreoffice",
"/usr/local/lib/libreoffice",
"/opt/libreoffice",
"/usr/lib64/openoffice",
"/usr/lib64/openoffice.org3",
"/usr/lib64/openoffice.org",
"/usr/lib/openoffice",
"/usr/lib/openoffice.org3",
"/usr/lib/openoffice.org",
"/opt/openoffice4",
"/opt/openoffice.org3");
}
}
private static File findOfficeHome(final String executablePath, final String... homePaths) {
return Stream.of(homePaths)
.filter(homePath -> Files.isRegularFile(Paths.get(homePath, executablePath)))
.findFirst()
.map(File::new)
.orElse(null);
}
}

View File

@@ -0,0 +1,86 @@
package cn.keking.utils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.extractor.ExtractorFactory;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
/**
* Office工具类
*
* @author ylyue
* @since 2022/7/5
*/
public class OfficeUtils {
private static final String POI_INVALID_PASSWORD_MSG = "password";
/**
* 判断officeword,excel,ppt文件是否受密码保护
*
* @param path office文件路径
* @return 是否受密码保护
*/
public static boolean isPwdProtected(String path) {
InputStream propStream = null;
try {
propStream = Files.newInputStream(Paths.get(path));
ExtractorFactory.createExtractor(propStream);
} catch (IOException | EncryptedDocumentException e) {
if (e.getMessage().toLowerCase().contains(POI_INVALID_PASSWORD_MSG)) {
return true;
}
} catch (Exception e) {
Throwable[] throwableArray = ExceptionUtils.getThrowables(e);
for (Throwable throwable : throwableArray) {
if (throwable instanceof IOException || throwable instanceof EncryptedDocumentException) {
if (e.getMessage().toLowerCase().contains(POI_INVALID_PASSWORD_MSG)) {
return true;
}
}
}
}finally {
if(propStream!=null) {//如果文件输入流不是null
try {
propStream.close();//关闭文件输入流
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
/**
* 判断office文件是否可打开兼容
*
* @param path office文件路径
* @param password 文件密码
* @return 是否可打开(兼容)
*/
public static synchronized boolean isCompatible(String path, String password) {
InputStream propStream = null;
try {
propStream = Files.newInputStream(Paths.get(path));
Biff8EncryptionKey.setCurrentUserPassword(password);
ExtractorFactory.createExtractor(propStream);
} catch (Exception e) {
return false;
} finally {
Biff8EncryptionKey.setCurrentUserPassword(null);
if(propStream!=null) {//如果文件输入流不是null
try {
propStream.close();//关闭文件输入流
} catch (IOException e) {
e.printStackTrace();
}
}
}
return true;
}
}

View File

@@ -0,0 +1,133 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.service.ZtreeNodeVo;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author : Gao
* create : 2023-04-08
**/
public class RarUtils {
private static final String fileDir = ConfigConstants.getFileDir();
public static byte[] getUTF8BytesFromGBKString(String gbkStr) {
int n = gbkStr.length();
byte[] utfBytes = new byte[3 * n];
int k = 0;
for (int i = 0; i < n; i++) {
int m = gbkStr.charAt(i);
if (m < 128 && m >= 0) {
utfBytes[k++] = (byte) m;
continue;
}
utfBytes[k++] = (byte) (0xe0 | (m >> 12));
utfBytes[k++] = (byte) (0x80 | ((m >> 6) & 0x3f));
utfBytes[k++] = (byte) (0x80 | (m & 0x3f));
}
if (k < utfBytes.length) {
byte[] tmp = new byte[k];
System.arraycopy(utfBytes, 0, tmp, 0, k);
return tmp;
}
return utfBytes;
}
public static String getUtf8String(String str) {
if (str != null && str.length() > 0) {
String needEncodeCode = "ISO-8859-1";
String neeEncodeCode = "ISO-8859-2";
String gbkEncodeCode = "GBK";
try {
if (Charset.forName(needEncodeCode).newEncoder().canEncode(str)) {
str = new String(str.getBytes(needEncodeCode), StandardCharsets.UTF_8);
}
if (Charset.forName(neeEncodeCode).newEncoder().canEncode(str)) {
str = new String(str.getBytes(neeEncodeCode), StandardCharsets.UTF_8);
}
if (Charset.forName(gbkEncodeCode).newEncoder().canEncode(str)) {
str = new String(getUTF8BytesFromGBKString(str), StandardCharsets.UTF_8);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return str;
}
/**
* 判断是否是中日韩文字
*/
private static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
return ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS;
}
public static boolean judge(char c){
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z');
}
public static String specialSymbols(String str) {
//去除压缩包文件字符串中特殊符号
Pattern p = Pattern.compile("\\s|\t|\r|\n|\\+|#|&|=|\\p{P}");
// Pattern p = Pattern.compile("\\s|\\+|#|&|=|\\p{P}");
Matcher m = p.matcher(str);
return m.replaceAll("");
}
public static boolean isMessyCode(String strName) {
//去除字符串中的空格 制表符 换行 回车
strName = specialSymbols(strName);
//处理之后转换成字符数组
char[] ch = strName.trim().toCharArray();
for (char c : ch) {
//判断是否是数字或者英文字符
if (!judge(c)) {
//判断是否是中日韩文
if (!isChinese(c)) {
//如果不是数字或者英文字符也不是中日韩文则表示是乱码返回true
return true;
}
}
}
//表示不是乱码 返回false
return false;
}
/**
* 读取文件目录树
*/
public static List<ZtreeNodeVo> getTree(String rootPath) {
List<ZtreeNodeVo> nodes = new ArrayList<>();
File file = new File(fileDir+rootPath);
ZtreeNodeVo node = traverse(file);
nodes.add(node);
return nodes;
}
private static ZtreeNodeVo traverse(File file) {
ZtreeNodeVo pathNodeVo = new ZtreeNodeVo();
pathNodeVo.setId(file.getAbsolutePath().replace(fileDir, "").replace("\\", "/"));
pathNodeVo.setName(file.getName());
pathNodeVo.setPid(file.getParent().replace(fileDir, "").replace("\\", "/"));
if (file.isDirectory()) {
List<ZtreeNodeVo> subNodeVos = new ArrayList<>();
File[] subFiles = file.listFiles();
if (subFiles == null) {
return pathNodeVo;
}
for (File subFile : subFiles) {
ZtreeNodeVo subNodeVo = traverse(subFile);
subNodeVos.add(subNodeVo);
}
pathNodeVo.setChildren(subNodeVos);
}
return pathNodeVo;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
package cn.keking.utils;
import javax.net.ssl.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* @author 鞠玉果
*/
public class SslUtils {
private static void trustAllHttpsCertificates() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[1];
TrustManager tm = new miTM();
trustAllCerts[0] = tm;
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
}
static class miTM implements TrustManager, X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
}
/**
* 忽略HTTPS请求的SSL证书必须在openConnection之前调用
*/
public static void ignoreSsl() throws Exception {
HostnameVerifier hv = (urlHostName, session) -> true;
trustAllHttpsCertificates();
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
}

View File

@@ -0,0 +1,82 @@
package cn.keking.utils;
import java.util.BitSet;
public class UrlEncoderUtils {
private static BitSet dontNeedEncoding;
static {
dontNeedEncoding = new BitSet(256);
int i;
for (i = 'a'; i <= 'z'; i++) {
dontNeedEncoding.set(i);
}
for (i = 'A'; i <= 'Z'; i++) {
dontNeedEncoding.set(i);
}
for (i = '0'; i <= '9'; i++) {
dontNeedEncoding.set(i);
}
dontNeedEncoding.set('+');
/**
* 这里会有误差,比如输入一个字符串 123+456,它到底是原文就是123+456还是123 456做了urlEncode后的内容呢<br>
* 其实问题是一样的比如遇到123%2B456,它到底是原文即使如此还是123+456 urlEncode后的呢 <br>
* 在这里我认为只要符合urlEncode规范的就当作已经urlEncode过了<br>
* 毕竟这个方法的初衷就是判断string是否urlEncode过<br>
*/
dontNeedEncoding.set('-');
dontNeedEncoding.set('_');
dontNeedEncoding.set('.');
dontNeedEncoding.set('*');
}
/**
* 判断str是否urlEncoder.encode过<br>
* 经常遇到这样的情况拿到一个URL,但是搞不清楚到底要不要encode.<Br>
* 不做encode吧担心出错做encode吧又怕重复了<Br>
*
* @param str
* @return
*/
public static boolean hasUrlEncoded(String str) {
/**
* 支持JAVA的URLEncoder.encode出来的string做判断。 即: 将' '转成'+' <br>
* 0-9a-zA-Z保留 <br>
* '-''_''.''*'保留 <br>
* 其他字符转成%XX的格式X是16进制的大写字符范围是[0-9A-F]
*/
boolean needEncode = false;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (dontNeedEncoding.get((int) c)) {
continue;
}
if (c == '%' && (i + 2) < str.length()) {
// 判断是否符合urlEncode规范
char c1 = str.charAt(++i);
char c2 = str.charAt(++i);
if (isDigit16Char(c1) && isDigit16Char(c2)) {
continue;
}
}
// 其他字符肯定需要urlEncode
needEncode = true;
break;
}
return !needEncode;
}
/**
* 判断c是否是16进制的字符
*
* @param c
* @return
*/
private static boolean isDigit16Char(char c) {
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F');
}
}

View File

@@ -0,0 +1,372 @@
package cn.keking.utils;
import io.mola.galimatias.GalimatiasParseException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Base64Utils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.HtmlUtils;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author : kl
* create : 2020-12-27 1:30 上午
**/
public class WebUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(WebUtils.class);
private static final String BASE64_MSG = "base64";
/**
* 获取标准的URL
*
* @param urlStr url
* @return 标准的URL
*/
public static URL normalizedURL(String urlStr) throws GalimatiasParseException, MalformedURLException {
return io.mola.galimatias.URL.parse(urlStr).toJavaURL();
}
/**
* 对文件名进行编码
*
*/
public static String encodeFileName(String name) {
try {
name = URLEncoder.encode(name, "UTF-8").replaceAll("\\+", "%20");
} catch (UnsupportedEncodingException e) {
return null;
}
return name;
}
/**
* 去除fullfilename参数
*
* @param urlStr
* @return
*/
public static String clearFullfilenameParam(String urlStr) {
// 去除特定参数字段
Pattern pattern = Pattern.compile("(&fullfilename=[^&]*)");
Matcher matcher = pattern.matcher(urlStr);
return matcher.replaceAll("");
}
/**
* 对URL进行编码
*/
public static String urlEncoderencode(String urlStr) {
String fullFileName = getUrlParameterReg(urlStr, "fullfilename"); //获取流文件名
if (org.springframework.util.StringUtils.hasText(fullFileName)) {
// 移除fullfilename参数
urlStr = clearFullfilenameParam(urlStr);
} else {
fullFileName = getFileNameFromURL(urlStr); //获取文件名
}
if (!UrlEncoderUtils.hasUrlEncoded(fullFileName)) { //判断文件名是否转义
try {
urlStr = URLEncoder.encode(urlStr, "UTF-8").replaceAll("\\+", "%20").replaceAll("%3A", ":").replaceAll("%2F", "/").replaceAll("%3F", "?").replaceAll("%26", "&").replaceAll("%3D", "=");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return urlStr;
}
/**
* 获取url中的参数
*
* @param url url
* @param name 参数名
* @return 参数值
*/
public static String getUrlParameterReg(String url, String name) {
Map<String, String> mapRequest = new HashMap<>();
String strUrlParam = truncateUrlPage(url);
if (strUrlParam == null) {
return "";
}
//每个键值为一组
String[] arrSplit = strUrlParam.split("[&]");
for (String strSplit : arrSplit) {
String[] arrSplitEqual = strSplit.split("[=]");
//解析出键值
if (arrSplitEqual.length > 1) {
//正确解析
mapRequest.put(arrSplitEqual[0], arrSplitEqual[1]);
} else if (!arrSplitEqual[0].equals("")) {
//只有参数没有值,不加入
mapRequest.put(arrSplitEqual[0], "");
}
}
return mapRequest.get(name);
}
/**
* 去掉url中的路径留下请求参数部分
*
* @param strURL url地址
* @return url请求参数部分
*/
private static String truncateUrlPage(String strURL) {
String strAllParam = null;
strURL = strURL.trim();
String[] arrSplit = strURL.split("[?]");
if (strURL.length() > 1) {
if (arrSplit.length > 1) {
if (arrSplit[1] != null) {
strAllParam = arrSplit[1];
}
}
}
return strAllParam;
}
/**
* 从url中剥离出文件名
*
* @param url 格式如http://www.com.cn/20171113164107_月度绩效表模板(新).xls?UCloudPublicKey=ucloudtangshd@weifenf.com14355492830001993909323&Expires=&Signature=I D1NOFtAJSPT16E6imv6JWuq0k=
* @return 文件名
*/
public static String getFileNameFromURL(String url) {
if (url.toLowerCase().startsWith("file:")) {
try {
URL urlObj = new URL(url);
url = urlObj.getPath().substring(1);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
}
/**
* 从url中剥离出文件名
* @param file 文件
* @return 文件名
*/
public static String getFileNameFromMultipartFile(MultipartFile file) {
String fileName = file.getOriginalFilename();
//判断是否为IE浏览器的文件名IE浏览器下文件名会带有盘符信
// escaping dangerous characters to prevent XSS
assert fileName != null;
fileName = HtmlUtils.htmlEscape(fileName, KkFileUtils.DEFAULT_FILE_ENCODING);
// Check for Unix-style path
int unixSep = fileName.lastIndexOf('/');
// Check for Windows-style path
int winSep = fileName.lastIndexOf('\\');
// Cut off at latest possible point
int pos = (Math.max(winSep, unixSep));
if (pos != -1) {
fileName = fileName.substring(pos + 1);
}
return fileName;
}
/**
* 从url中获取文件后缀
*
* @param url url
* @return 文件后缀
*/
public static String suffixFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return KkFileUtils.suffixFromFileName(fileName);
}
/**
* 对url中的文件名进行UTF-8编码
*
* @param url url
* @return 文件名编码后的url
*/
public static String encodeUrlFileName(String url) {
String encodedFileName;
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
int fileNameStartIndex = noQueryUrl.lastIndexOf('/') + 1;
int fileNameEndIndex = noQueryUrl.lastIndexOf('.');
if (fileNameEndIndex < fileNameStartIndex) {
return url;
}
try {
encodedFileName = URLEncoder.encode(noQueryUrl.substring(fileNameStartIndex, fileNameEndIndex), "UTF-8");
} catch (UnsupportedEncodingException e) {
return null;
}
return url.substring(0, fileNameStartIndex) + encodedFileName + url.substring(fileNameEndIndex);
}
/**
* 从 ServletRequest 获取预览的源 url , 已 base64 解码
*
* @param request 请求 request
* @return url
*/
public static String getSourceUrl(ServletRequest request) {
String url = request.getParameter("url");
String urls = request.getParameter("urls");
String currentUrl = request.getParameter("currentUrl");
String urlPath = request.getParameter("urlPath");
if (StringUtils.isNotBlank(url)) {
return decodeUrl(url);
}
if (StringUtils.isNotBlank(currentUrl)) {
return decodeUrl(currentUrl);
}
if (StringUtils.isNotBlank(urlPath)) {
return decodeUrl(urlPath);
}
if (StringUtils.isNotBlank(urls)) {
urls = decodeUrl(urls);
String[] images = urls.split("\\|");
return images[0];
}
return null;
}
/**
* 判断地址是否正确
* 高 2022/12/17
*/
public static boolean isValidUrl(String url) {
String regStr = "^((https|http|ftp|rtsp|mms|file)://)";//[.?*]表示匹配的就是本身
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(url);
return matcher.find();
}
/**
* 将 Base64 字符串解码再解码URL参数, 默认使用 UTF-8
* @param source 原始 Base64 字符串
* @return decoded string
*
* aHR0cHM6Ly9maWxlLmtla2luZy5jbi9kZW1vL%2BS4reaWhy5wcHR4 -> https://file.keking.cn/demo/%E4%B8%AD%E6%96%87.pptx -> https://file.keking.cn/demo/中文.pptx
*/
public static String decodeUrl(String source) {
String url = decodeBase64String(source, StandardCharsets.UTF_8);
if (! StringUtils.isNotBlank(url)){
return null;
}
return url;
}
/**
* 将 Base64 字符串使用指定字符集解码
* @param source 原始 Base64 字符串
* @param charsets 字符集
* @return decoded string
*/
public static String decodeBase64String(String source, Charset charsets) {
/*
* url 传入的参数里加号会被替换成空格,导致解析出错,这里需要把空格替换回加号
* 有些 Base64 实现可能每 76 个字符插入换行符,也一并去掉
* https://github.com/kekingcn/kkFileView/pull/340
*/
try {
return new String(Base64Utils.decodeFromString(source.replaceAll(" ", "+").replaceAll("\n", "")), charsets);
} catch (Exception e) {
if (e.getMessage().toLowerCase().contains(BASE64_MSG)) {
LOGGER.error("url解码异常接入方法错误未使用BASE64");
}else {
LOGGER.error("url解码异常其他错误", e);
}
return null;
}
}
/**
* 获取 url 的 host
* @param urlStr url
* @return host
*/
public static String getHost(String urlStr) {
try {
URL url = new URL(urlStr);
return url.getHost().toLowerCase();
} catch (MalformedURLException ignored) {
}
return null;
}
/**
* 获取 session 中的 String 属性
* @param request 请求
* @return 属性值
*/
public static String getSessionAttr(HttpServletRequest request, String key) {
HttpSession session = request.getSession();
if (session == null) {
return null;
}
Object value = session.getAttribute(key);
if (value == null) {
return null;
}
return value.toString();
}
/**
* 获取 session 中的 long 属性
* @param request 请求
* @param key 属性名
* @return 属性值
*/
public static long getLongSessionAttr(HttpServletRequest request, String key) {
String value = getSessionAttr(request, key);
if (value == null) {
return 0;
}
return Long.parseLong(value);
}
/**
* session 中设置属性
* @param request 请求
* @param key 属性名
*/
public static void setSessionAttr(HttpServletRequest request, String key, Object value) {
HttpSession session = request.getSession();
if (session == null) {
return;
}
session.setAttribute(key, value);
}
/**
* 移除 session 中的属性
* @param request 请求
* @param key 属性名
*/
public static void removeSessionAttr(HttpServletRequest request, String key) {
HttpSession session = request.getSession();
if (session == null) {
return;
}
session.removeAttribute(key);
}
}

View File

@@ -0,0 +1,226 @@
package cn.keking.web.controller;
import cn.keking.config.ConfigConstants;
import cn.keking.model.ReturnResponse;
import cn.keking.utils.CaptchaUtil;
import cn.keking.utils.DateUtils;
import cn.keking.utils.KkFileUtils;
import cn.keking.utils.RarUtils;
import cn.keking.utils.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static cn.keking.utils.CaptchaUtil.CAPTCHA_CODE;
import static cn.keking.utils.CaptchaUtil.CAPTCHA_GENERATE_TIME;
/**
* @author yudian-it
* 2017/12/1
*/
@RestController
public class FileController {
private final Logger logger = LoggerFactory.getLogger(FileController.class);
private final String fileDir = ConfigConstants.getFileDir();
private final String demoDir = "demo";
private final String demoPath = demoDir + File.separator;
public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!";
@PostMapping("/fileUpload")
public ReturnResponse<Object> fileUpload(@RequestParam("file") MultipartFile file) {
ReturnResponse<Object> checkResult = this.fileUploadCheck(file);
if (checkResult.isFailure()) {
return checkResult;
}
File outFile = new File(fileDir + demoPath);
if (!outFile.exists() && !outFile.mkdirs()) {
logger.error("创建文件夹【{}】失败,请检查目录权限!", fileDir + demoPath);
}
String fileName = checkResult.getContent().toString();
logger.info("上传文件:{}{}{}", fileDir, demoPath, fileName);
try (InputStream in = file.getInputStream(); OutputStream out = Files.newOutputStream(Paths.get(fileDir + demoPath + fileName))) {
StreamUtils.copy(in, out);
return ReturnResponse.success(null);
} catch (IOException e) {
logger.error("文件上传失败", e);
return ReturnResponse.failure();
}
}
@GetMapping("/deleteFile")
public ReturnResponse<Object> deleteFile(HttpServletRequest request, String fileName, String password) {
ReturnResponse<Object> checkResult = this.deleteFileCheck(request, fileName, password);
if (checkResult.isFailure()) {
return checkResult;
}
fileName = checkResult.getContent().toString();
File file = new File(fileDir + demoPath + fileName);
logger.info("删除文件:{}", file.getAbsolutePath());
if (file.exists() && !file.delete()) {
String msg = String.format("删除文件【%s】失败请检查目录权限", file.getPath());
logger.error(msg);
return ReturnResponse.failure(msg);
}
WebUtils.removeSessionAttr(request, CAPTCHA_CODE); //删除缓存验证码
return ReturnResponse.success();
}
/**
* 验证码方法
*/
@RequestMapping("/deleteFile/captcha")
public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (!ConfigConstants.getDeleteCaptcha()) {
return;
}
response.setContentType("image/jpeg");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", -1);
String captchaCode = WebUtils.getSessionAttr(request, CAPTCHA_CODE);
long captchaGenerateTime = WebUtils.getLongSessionAttr(request, CAPTCHA_GENERATE_TIME);
long timeDifference = DateUtils.calculateCurrentTimeDifference(captchaGenerateTime);
// 验证码为空且生成验证码超过50秒重新生成验证码
if (timeDifference > 50 && ObjectUtils.isEmpty(captchaCode)) {
captchaCode = CaptchaUtil.generateCaptchaCode();
// 更新验证码
WebUtils.setSessionAttr(request, CAPTCHA_CODE, captchaCode);
WebUtils.setSessionAttr(request, CAPTCHA_GENERATE_TIME, DateUtils.getCurrentSecond());
} else {
captchaCode = ObjectUtils.isEmpty(captchaCode) ? "wait" : captchaCode;
}
ServletOutputStream outputStream = response.getOutputStream();
ImageIO.write(CaptchaUtil.generateCaptchaPic(captchaCode), "jpeg", outputStream);
outputStream.close();
}
@GetMapping("/listFiles")
public List<Map<String, String>> getFiles() {
List<Map<String, String>> list = new ArrayList<>();
File file = new File(fileDir + demoPath);
if (file.exists()) {
File[] files = Objects.requireNonNull(file.listFiles());
Arrays.sort(files, (f1, f2) -> Long.compare(f2.lastModified(), f1.lastModified()));
Arrays.stream(files).forEach(file1 -> {
Map<String, String> fileName = new HashMap<>();
fileName.put("fileName", demoDir + "/" + file1.getName());
list.add(fileName);
});
}
return list;
}
/**
* 上传文件前校验
*
* @param file 文件
* @return 校验结果
*/
private ReturnResponse<Object> fileUploadCheck(MultipartFile file) {
if (ConfigConstants.getFileUploadDisable()) {
return ReturnResponse.failure("文件传接口已禁用");
}
String fileName = WebUtils.getFileNameFromMultipartFile(file);
if (fileName.lastIndexOf(".") == -1) {
return ReturnResponse.failure("不允许上传的类型");
}
if (!KkFileUtils.isAllowedUpload(fileName)) {
return ReturnResponse.failure("不允许上传的文件类型: " + fileName);
}
if (KkFileUtils.isIllegalFileName(fileName)) {
return ReturnResponse.failure("不允许上传的文件名: " + fileName);
}
// 判断是否存在同名文件
if (existsFile(fileName)) {
return ReturnResponse.failure("存在同名文件,请先删除原有文件再次上传");
}
return ReturnResponse.success(fileName);
}
/**
* 删除文件前校验
*
* @param fileName 文件名
* @return 校验结果
*/
private ReturnResponse<Object> deleteFileCheck(HttpServletRequest request, String fileName, String password) {
if (ObjectUtils.isEmpty(fileName)) {
return ReturnResponse.failure("文件名为空,删除失败!");
}
try {
fileName = WebUtils.decodeUrl(fileName);
} catch (Exception ex) {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, fileName);
return ReturnResponse.failure(errorMsg + "删除失败!");
}
assert fileName != null;
if (fileName.contains("/")) {
fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
}
if (KkFileUtils.isIllegalFileName(fileName)) {
return ReturnResponse.failure("非法文件名,删除失败!");
}
if (ObjectUtils.isEmpty(password)) {
return ReturnResponse.failure("密码 or 验证码为空,删除失败!");
}
String expectedPassword = ConfigConstants.getDeleteCaptcha() ? WebUtils.getSessionAttr(request, CAPTCHA_CODE) : ConfigConstants.getPassword();
if (!password.equalsIgnoreCase(expectedPassword)) {
logger.error("删除文件【{}】失败,密码错误!", fileName);
return ReturnResponse.failure("删除文件失败,密码错误!");
}
return ReturnResponse.success(fileName);
}
@GetMapping("/directory")
public Object directory(String urls) {
String fileUrl;
try {
fileUrl = WebUtils.decodeUrl(urls);
} catch (Exception ex) {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
return ReturnResponse.failure(errorMsg);
}
if (KkFileUtils.isIllegalFileName(fileUrl)) {
return ReturnResponse.failure("不允许访问的路径:");
}
return RarUtils.getTree(fileUrl);
}
private boolean existsFile(String fileName) {
File file = new File(fileDir + demoPath + fileName);
return file.exists();
}
}

View File

@@ -0,0 +1,40 @@
package cn.keking.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* 页面跳转
* @author yudian-it
* @date 2017/12/27
*/
@Controller
public class IndexController {
@GetMapping( "/index")
public String go2Index(){
return "/main/index";
}
@GetMapping( "/record")
public String go2Record(){
return "/main/record";
}
@GetMapping( "/sponsor")
public String go2Sponsor(){
return "/main/sponsor";
}
@GetMapping( "/integrated")
public String go2Integrated(){
return "/main/integrated";
}
@GetMapping( "/")
public String root() {
return "/main/index";
}
}

View File

@@ -0,0 +1,183 @@
package cn.keking.web.controller;
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.KkFileUtils;
import cn.keking.utils.WebUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.opensagres.xdocreport.core.io.IOUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
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.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static cn.keking.service.FilePreview.PICTURE_FILE_PREVIEW_PAGE;
/**
* @author yudian-it
*/
@Controller
public class OnlinePreviewController {
public static final String BASE64_DECODE_ERROR_MSG = "Base64解码失败请检查你的 %s 是否采用 Base64 + urlEncode 双重编码了!";
private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class);
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FileHandlerService fileHandlerService;
private final OtherFilePreviewImpl otherFilePreview;
private static final RestTemplate restTemplate = new RestTemplate();
private static final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
private static final ObjectMapper mapper = new ObjectMapper();
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(String url, Model model, HttpServletRequest req) {
String fileUrl;
try {
fileUrl = WebUtils.decodeUrl(url);
} catch (Exception ex) {
String errorMsg = String.format(BASE64_DECODE_ERROR_MSG, "url");
return otherFilePreview.notSupportedFile(model, errorMsg);
}
FileAttribute fileAttribute = fileHandlerService.getFileAttribute(fileUrl, req); //这里不在进行URL 处理了
model.addAttribute("file", fileAttribute);
FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", fileUrl, fileAttribute.getType());
return filePreview.filePreviewHandle(WebUtils.urlEncoderencode(fileUrl), model, fileAttribute); //统一在这里处理 url
}
@GetMapping( "/picturesPreview")
public String picturesPreview(String urls, Model model, HttpServletRequest req) {
String fileUrls;
try {
fileUrls = WebUtils.decodeUrl(urls);
// 防止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(String urlPath, HttpServletResponse response,FileAttribute fileAttribute) throws IOException {
URL url;
try {
urlPath = WebUtils.decodeUrl(urlPath);
url = WebUtils.normalizedURL(urlPath);
} catch (Exception ex) {
logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath),ex);
return;
}
assert urlPath != null;
if (!urlPath.toLowerCase().startsWith("http") && !urlPath.toLowerCase().startsWith("https") && !urlPath.toLowerCase().startsWith("ftp")) {
logger.info("读取跨域文件异常可能存在非法访问urlPath{}", urlPath);
return;
}
InputStream inputStream = null;
logger.info("读取跨域pdf文件url{}", urlPath);
if (!urlPath.toLowerCase().startsWith("ftp:")) {
factory.setConnectionRequestTimeout(2000);
factory.setConnectTimeout(10000);
factory.setReadTimeout(72000);
HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new DefaultRedirectStrategy()).build();
factory.setHttpClient(httpClient);
restTemplate.setRequestFactory(factory);
RequestCallback requestCallback = request -> {
request.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));
String proxyAuthorization = fileAttribute.getKkProxyAuthorization();
if(StringUtils.hasText(proxyAuthorization)){
Map<String,String> proxyAuthorizationMap = mapper.readValue(proxyAuthorization, Map.class);
proxyAuthorizationMap.forEach((key, value) -> request.getHeaders().set(key, value));
}
};
try {
restTemplate.execute(url.toURI(), HttpMethod.GET, requestCallback, fileResponse -> {
IOUtils.copy(fileResponse.getBody(), response.getOutputStream());
return null;
});
} catch (Exception e) {
System.out.println(e);
}
}else{
try {
if(urlPath.contains(".svg")) {
response.setContentType("image/svg+xml");
}
inputStream = (url).openStream();
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(String url) {
logger.info("添加转码队列url{}", url);
cacheService.addQueueTask(url);
return "success";
}
}

View File

@@ -0,0 +1,102 @@
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;
import java.io.IOException;
/**
* @author chenjh
* @since 2020/5/13 18:34
*/
public class AttributeSetFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
this.setWatermarkAttribute(request);
this.setFileAttribute(request);
filterChain.doFilter(request, response);
}
/**
* 设置办公文具预览逻辑需要的属性
* @param request request
*/
private void setFileAttribute(ServletRequest request){
HttpServletRequest httpRequest = (HttpServletRequest) request;
request.setAttribute("pdfPresentationModeDisable", ConfigConstants.getPdfPresentationModeDisable());
request.setAttribute("pdfOpenFileDisable", ConfigConstants.getPdfOpenFileDisable());
request.setAttribute("pdfPrintDisable", ConfigConstants.getPdfPrintDisable());
request.setAttribute("pdfDownloadDisable", ConfigConstants.getPdfDownloadDisable());
request.setAttribute("pdfBookmarkDisable", ConfigConstants.getPdfBookmarkDisable());
request.setAttribute("pdfDisableEditing", ConfigConstants.getPdfDisableEditing());
request.setAttribute("switchDisabled", ConfigConstants.getOfficePreviewSwitchDisabled());
request.setAttribute("fileUploadDisable", ConfigConstants.getFileUploadDisable());
request.setAttribute("beian", ConfigConstants.getBeian());
request.setAttribute("size", ConfigConstants.maxSize());
request.setAttribute("deleteCaptcha", ConfigConstants.getDeleteCaptcha());
request.setAttribute("homePageNumber", ConfigConstants.getHomePageNumber());
request.setAttribute("homePagination", ConfigConstants.getHomePagination());
request.setAttribute("homePageSize", ConfigConstants.getHomePageSize());
request.setAttribute("homeSearch", ConfigConstants.getHomeSearch());
}
/**
* 设置水印属性
* @param request request
*/
private void setWatermarkAttribute(ServletRequest request) {
String watermarkTxt= KkFileUtils.htmlEscape(request.getParameter("watermarkTxt"));
request.setAttribute("watermarkTxt", watermarkTxt != null ? watermarkTxt : WatermarkConfigConstants.getWatermarkTxt());
String watermarkXSpace = KkFileUtils.htmlEscape(request.getParameter("watermarkXSpace"));
if (!KkFileUtils.isInteger(watermarkXSpace)){
watermarkXSpace =null;
}
request.setAttribute("watermarkXSpace", watermarkXSpace != null ? watermarkXSpace : WatermarkConfigConstants.getWatermarkXSpace());
String watermarkYSpace = KkFileUtils.htmlEscape(request.getParameter("watermarkYSpace"));
if (!KkFileUtils.isInteger(watermarkYSpace)){
watermarkYSpace =null;
}
request.setAttribute("watermarkYSpace", watermarkYSpace != null ? watermarkYSpace : WatermarkConfigConstants.getWatermarkYSpace());
String watermarkFont = KkFileUtils.htmlEscape(request.getParameter("watermarkFont"));
request.setAttribute("watermarkFont", watermarkFont != null ? watermarkFont : WatermarkConfigConstants.getWatermarkFont());
String watermarkFontsize = KkFileUtils.htmlEscape(request.getParameter("watermarkFontsize"));
request.setAttribute("watermarkFontsize", watermarkFontsize != null ? watermarkFontsize : WatermarkConfigConstants.getWatermarkFontsize());
String watermarkColor = KkFileUtils.htmlEscape(request.getParameter("watermarkColor"));
request.setAttribute("watermarkColor", watermarkColor != null ? watermarkColor : WatermarkConfigConstants.getWatermarkColor());
String watermarkAlpha = KkFileUtils.htmlEscape(request.getParameter("watermarkAlpha"));
if (!KkFileUtils.isInteger(watermarkAlpha)){
watermarkAlpha =null;
}
request.setAttribute("watermarkAlpha", watermarkAlpha != null ? watermarkAlpha : WatermarkConfigConstants.getWatermarkAlpha());
String watermarkWidth = KkFileUtils.htmlEscape(request.getParameter("watermarkWidth"));
if (!KkFileUtils.isInteger(watermarkWidth)){
watermarkWidth =null;
}
request.setAttribute("watermarkWidth", watermarkWidth != null ? watermarkWidth : WatermarkConfigConstants.getWatermarkWidth());
String watermarkHeight = KkFileUtils.htmlEscape(request.getParameter("watermarkHeight"));
if (!KkFileUtils.isInteger(watermarkHeight)){
watermarkHeight =null;
}
request.setAttribute("watermarkHeight", watermarkHeight != null ? watermarkHeight : WatermarkConfigConstants.getWatermarkHeight());
String watermarkAngle = KkFileUtils.htmlEscape(request.getParameter("watermarkAngle"));
if (!KkFileUtils.isInteger(watermarkAngle)){
watermarkAngle =null;
}
request.setAttribute("watermarkAngle", watermarkAngle != null ? watermarkAngle : WatermarkConfigConstants.getWatermarkAngle());
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,68 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author chenjh
* @since 2020/5/13 18:27
*/
public class BaseUrlFilter implements Filter {
private static String BASE_URL;
public static String getBaseUrl() {
String baseUrl;
try {
baseUrl = (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl", 0);
} catch (Exception e) {
baseUrl = BASE_URL;
}
return baseUrl;
}
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
String baseUrl;
String configBaseUrl = ConfigConstants.getBaseUrl();
final HttpServletRequest servletRequest = (HttpServletRequest) request;
//1、支持通过 http header 中 X-Base-Url 来动态设置 baseUrl 以支持多个域名/项目的共享使用
final String urlInHeader = servletRequest.getHeader("X-Base-Url");
if (StringUtils.isNotEmpty(urlInHeader)) {
baseUrl = urlInHeader;
} else if (configBaseUrl != null && !ConfigConstants.DEFAULT_VALUE.equalsIgnoreCase(configBaseUrl)) {
//2、如果配置文件中配置了 baseUrl 且不为 default 则以配置文件为准
baseUrl = configBaseUrl;
} else {
//3、默认动态拼接 baseUrl
baseUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ servletRequest.getContextPath() + "/";
}
if (!baseUrl.endsWith("/")) {
baseUrl = baseUrl.concat("/");
}
BASE_URL = baseUrl;
request.setAttribute("baseUrl", baseUrl);
filterChain.doFilter(request, response);
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,28 @@
package cn.keking.web.filter;
import javax.servlet.*;
import java.io.IOException;
/**
*
* @author yudian-it
* @date 2017/11/30
*/
public class ChinesePathFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,30 @@
package cn.keking.web.filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Configuration
public class SecurityFilterProxy extends OncePerRequestFilter {
private String NOT_ALLOW_METHODS = "TRACE";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
if((","+NOT_ALLOW_METHODS+",").indexOf(","+request.getMethod().toUpperCase()+",") > -1) {
response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
response.setHeader("Content-Type", "text/html; charset=iso-8859-1");
response.getWriter().println("Method Not Allowed");
return;
}
super.doFilter(request, response, filterChain);
}
}

View File

@@ -0,0 +1,78 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import cn.keking.utils.WebUtils;
import io.mola.galimatias.GalimatiasParseException;
import org.jodconverter.core.util.OSUtils;
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;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
/**
* @author : kl (http://kailing.pub)
* @since : 2022-05-25 17:45
*/
public class TrustDirFilter implements Filter {
private String notTrustDirView;
private final Logger logger = LoggerFactory.getLogger(TrustDirFilter.class);
@Override
public void init(FilterConfig filterConfig) {
ClassPathResource classPathResource = new ClassPathResource("web/notTrustDir.html");
try {
classPathResource.getInputStream();
byte[] bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
this.notTrustDirView = new String(bytes, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String url = WebUtils.getSourceUrl(request);
if (!allowPreview(url)) {
response.getWriter().write(this.notTrustDirView);
response.getWriter().close();
} else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
private boolean allowPreview(String urlPath) {
//判断URL是否合法
if(!StringUtils.hasText(urlPath) || !WebUtils.isValidUrl(urlPath)) {
return false ;
}
try {
URL url = WebUtils.normalizedURL(urlPath);
if ("file".equals(url.getProtocol().toLowerCase(Locale.ROOT))) {
String filePath = URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8.name());
if (OSUtils.IS_OS_WINDOWS) {
filePath = filePath.replaceAll("/", "\\\\");
}
return filePath.startsWith(ConfigConstants.getFileDir()) || filePath.startsWith(ConfigConstants.getLocalPreviewDir());
}
return true;
} catch (IOException | GalimatiasParseException e) {
logger.error("解析URL异常url{}", urlPath, e);
return false;
}
}
}

View File

@@ -0,0 +1,68 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import cn.keking.utils.WebUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
/**
* @author chenjh
* @since 2020/2/18 19:13
*/
public class TrustHostFilter implements Filter {
private String notTrustHostHtmlView;
@Override
public void init(FilterConfig filterConfig) {
ClassPathResource classPathResource = new ClassPathResource("web/notTrustHost.html");
try {
classPathResource.getInputStream();
byte[] bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
this.notTrustHostHtmlView = new String(bytes, StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String url = WebUtils.getSourceUrl(request);
String host = WebUtils.getHost(url);
assert host != null;
if (isNotTrustHost(host)) {
String html = this.notTrustHostHtmlView.replace("${current_host}", host);
response.getWriter().write(html);
response.getWriter().close();
} else {
chain.doFilter(request, response);
}
}
public boolean isNotTrustHost(String host) {
if (CollectionUtils.isNotEmpty(ConfigConstants.getNotTrustHostSet())) {
return ConfigConstants.getNotTrustHostSet().contains(host);
}
if (CollectionUtils.isNotEmpty(ConfigConstants.getTrustHostSet())) {
return !ConfigConstants.getTrustHostSet().contains(host);
}
return false;
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,52 @@
package cn.keking.web.filter;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @date 2023/11/30
*/
public class UrlCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String servletPath = httpServletRequest.getServletPath();
boolean redirect = false;
// servletPath 中不能包含 //
if (servletPath.contains("//")) {
servletPath = servletPath.replaceAll("//+", "/");
redirect = true;
}
// 不能以 / 结尾,同时考虑 **首页** 的特殊性
if (servletPath.endsWith("/") && servletPath.length() > 1) {
servletPath = servletPath.substring(0, servletPath.length() - 1);
redirect = true;
}
if (redirect) {
String redirectUrl;
if (StringUtils.isBlank(BaseUrlFilter.getBaseUrl())) {
// 正常 BaseUrlFilter 有限此 Filter 执行,不会执行到此
redirectUrl = httpServletRequest.getContextPath() + servletPath;
} else {
if (BaseUrlFilter.getBaseUrl().endsWith("/") && servletPath.startsWith("/")) {
// BaseUrlFilter.getBaseUrl() 以 / 结尾servletPath 以 / 开头,需再去除一次 //
redirectUrl = BaseUrlFilter.getBaseUrl() + servletPath.substring(1);
} else {
redirectUrl = BaseUrlFilter.getBaseUrl() + servletPath;
}
}
((HttpServletResponse) response).sendRedirect(redirectUrl + "?" + httpServletRequest.getQueryString());
} else {
chain.doFilter(request, response);
}
}
}