调整项目模块,jodconverter-core重命名为office-plugin。jdocnverter-web重命名为server

This commit is contained in:
chenkailing
2020-12-26 17:30:24 +08:00
committed by kl
parent 0f4f1d580b
commit 41cdc227b3
4382 changed files with 2240 additions and 83394 deletions

View File

@@ -0,0 +1,22 @@
package cn.keking;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
@ComponentScan(value = "cn.keking.*")
public class ServerMain {
public static void main(String[] args) {
ServerMain.staticInitSystemProperty();
SpringApplication.run(ServerMain.class, args);
}
private static void staticInitSystemProperty(){
//pdfbox兼容低版本jdk
System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider");
}
}

View File

@@ -0,0 +1,217 @@
package cn.keking.config;
import org.artofsolving.jodconverter.util.ConfigUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.Arrays;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.Set;
/**
* @author: chenjh
* @since: 2019/4/10 17:22
*/
@Component
public class ConfigConstants {
private static Boolean CACHE_ENABLED;
private static String[] SIM_TEXT = {};
private static String[] MEDIA = {};
private static String OFFICE_PREVIEW_TYPE;
private static String OFFICE_PREVIEW_SWITCH_DISABLED;
private static String FTP_USERNAME;
private static String FTP_PASSWORD;
private static String FTP_CONTROL_ENCODING;
private static String BASE_URL;
private static String FILE_DIR = ConfigUtils.getHomePath() + File.separator + "file" + File.separator;
private static CopyOnWriteArraySet<String> TRUST_HOST_SET;
private static String PDF_DOWNLOAD_DISABLE;
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";
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_BASE_URL = "default";
public static final String DEFAULT_FILE_DIR_VALUE = "default";
public static final String DEFAULT_TRUST_HOST = "default";
public static final String DEFAULT_PDF_DOWNLOAD_DISABLE = "true";
public static Boolean isCacheEnabled() {
return CACHE_ENABLED;
}
@Value("${cache.enabled:true}")
public void setCacheEnabled(String cacheEnabled) {
setCacheEnabledValueValue(Boolean.parseBoolean(cacheEnabled));
}
public static void setCacheEnabledValueValue(Boolean cacheEnabled) {
CACHE_ENABLED = cacheEnabled;
}
public static String[] getSimText() {
return SIM_TEXT;
}
@Value("${simText:txt,html,htm,asp,jsp,xml,json,properties,md,gitignore,log,java,py,c,cpp,sql,sh,bat,m,bas,prg,cmd}")
public void setSimText(String simText) {
String[] simTextArr = simText.split(",");
setSimTextValue(simTextArr);
}
public static void setSimTextValue(String[] simText) {
SIM_TEXT = simText;
}
public static String[] getMedia() {
return MEDIA;
}
@Value("${media:mp3,wav,mp4,flv}")
public void setMedia(String media) {
String[] mediaArr = media.split(",");
setMediaValue(mediaArr);
}
public static void setMediaValue(String[] Media) {
MEDIA = Media;
}
public static String getOfficePreviewType() {
return OFFICE_PREVIEW_TYPE;
}
@Value("${office.preview.type:image}")
public void setOfficePreviewType(String officePreviewType) {
setOfficePreviewTypeValue(officePreviewType);
}
public static void setOfficePreviewTypeValue(String officePreviewType) {
OFFICE_PREVIEW_TYPE = officePreviewType;
}
public static String getFtpUsername() {
return FTP_USERNAME;
}
@Value("${ftp.username:}")
public void setFtpUsername(String ftpUsername) {
setFtpUsernameValue(ftpUsername);
}
public static void setFtpUsernameValue(String ftpUsername) {
FTP_USERNAME = ftpUsername;
}
public static String getFtpPassword() {
return FTP_PASSWORD;
}
@Value("${ftp.password:}")
public void setFtpPassword(String ftpPassword) {
setFtpPasswordValue(ftpPassword);
}
public static void setFtpPasswordValue(String ftpPassword) {
FTP_PASSWORD = ftpPassword;
}
public static String getFtpControlEncoding() {
return FTP_CONTROL_ENCODING;
}
@Value("${ftp.control.encoding:UTF-8}")
public void setFtpControlEncoding(String ftpControlEncoding) {
setFtpControlEncodingValue(ftpControlEncoding);
}
public static void setFtpControlEncodingValue(String ftpControlEncoding) {
FTP_CONTROL_ENCODING = ftpControlEncoding;
}
public static String getBaseUrl() {
return BASE_URL;
}
@Value("${base.url:default}")
public void setBaseUrl(String baseUrl) {
setBaseUrlValue(baseUrl);
}
public static void setBaseUrlValue(String baseUrl) {
BASE_URL = baseUrl;
}
public static String getFileDir() {
return FILE_DIR;
}
@Value("${file.dir:default}")
public void setFileDir(String fileDir) {
setFileDirValue(fileDir);
}
public static void setFileDirValue(String fileDir) {
if (!DEFAULT_FILE_DIR_VALUE.equalsIgnoreCase(fileDir)) {
if (!fileDir.endsWith(File.separator)) {
fileDir = fileDir + File.separator;
}
FILE_DIR = fileDir;
}
}
@Value("${trust.host:default}")
public void setTrustHost(String trustHost) {
setTrustHostValue(trustHost);
}
public static void setTrustHostValue(String trustHost) {
CopyOnWriteArraySet<String> trustHostSet;
if (DEFAULT_TRUST_HOST.equalsIgnoreCase(trustHost)) {
trustHostSet = new CopyOnWriteArraySet<>();
} else {
String[] trustHostArray = trustHost.toLowerCase().split(",");
trustHostSet = new CopyOnWriteArraySet<>(Arrays.asList(trustHostArray));
setTrustHostSet(trustHostSet);
}
setTrustHostSet(trustHostSet);
}
public static Set<String> getTrustHostSet() {
return TRUST_HOST_SET;
}
private static void setTrustHostSet(CopyOnWriteArraySet<String> trustHostSet) {
ConfigConstants.TRUST_HOST_SET = trustHostSet;
}
public static String getPdfDownloadDisable() {
return PDF_DOWNLOAD_DISABLE;
}
@Value("${pdf.download.disable:true}")
public void setPdfDownloadDisable(String pdfDownloadDisable) {
setPdfDownloadDisableValue(pdfDownloadDisable);
}
public static void setPdfDownloadDisableValue(String pdfDownloadDisable) {
PDF_DOWNLOAD_DISABLE = pdfDownloadDisable;
}
public static String getOfficePreviewSwitchDisabled() {
return OFFICE_PREVIEW_SWITCH_DISABLED;
}
@Value("${office.preview.switch.disabled:true}")
public void setOfficePreviewSwitchDisabled(String officePreviewSwitchDisabled) {
OFFICE_PREVIEW_SWITCH_DISABLED = officePreviewSwitchDisabled;
}
public static void setOfficePreviewSwitchDisabledValue(String officePreviewSwitchDisabled) {
OFFICE_PREVIEW_SWITCH_DISABLED = officePreviewSwitchDisabled;
}
}

View File

@@ -0,0 +1,19 @@
package cn.keking.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author chenjh
* @since 2020/5/18 13:41
*/
@Configuration
public class RFCConfig {
@Bean
public Boolean setRequestTargetAllow() {
// RFC 7230RFC 3986规范不允许url相关特殊字符手动指定Tomcat url允许特殊符号 如{}做入参其他符号按需添加。见tomcat的HttpParser源码。
System.setProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow", "|{}");
return true;
}
}

View File

@@ -0,0 +1,242 @@
package cn.keking.config;
import io.netty.channel.nio.NioEventLoopGroup;
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(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,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,27 @@
package cn.keking.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author: chenjh
* @since: 2019/4/16 20:04
*/
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
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);
super.addResourceHandlers(registry);
}
}

View File

@@ -0,0 +1,157 @@
package cn.keking.extend;
import org.artofsolving.jodconverter.document.DocumentFamily;
import org.artofsolving.jodconverter.document.DocumentFormat;
import org.artofsolving.jodconverter.document.SimpleDocumentFormatRegistry;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 重写了DefaultDocumentFormatRegistry类因为要添加自定义行为比如字符编码。。。
* @author yudian-it
* @date 2017/12/5
*/
public class ControlDocumentFormatRegistry extends SimpleDocumentFormatRegistry {
public ControlDocumentFormatRegistry() {
DocumentFormat pdf = new DocumentFormat("Portable Document Format", "pdf", "application/pdf");
pdf.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "writer_pdf_Export"));
pdf.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "calc_pdf_Export"));
pdf.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "impress_pdf_Export"));
pdf.setStoreProperties(DocumentFamily.DRAWING, Collections.singletonMap("FilterName", "draw_pdf_Export"));
addFormat(pdf);
DocumentFormat swf = new DocumentFormat("Macromedia Flash", "swf", "application/x-shockwave-flash");
swf.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "impress_flash_Export"));
swf.setStoreProperties(DocumentFamily.DRAWING, Collections.singletonMap("FilterName", "draw_flash_Export"));
addFormat(swf);
// disabled because it's not always available
//DocumentFormat xhtml = new DocumentFormat("XHTML", "xhtml", "application/xhtml+xml");
//xhtml.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "XHTML Writer File"));
//xhtml.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "XHTML Calc File"));
//xhtml.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "XHTML Impress File"));
//addFormat(xhtml);
DocumentFormat html = new DocumentFormat("HTML", "html", "text/html");
// HTML is treated as Text when supplied as input, but as an output it is also
// available for exporting Spreadsheet and Presentation formats
html.setInputFamily(DocumentFamily.TEXT);
html.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "HTML (StarWriter)"));
Map<String,Object> htmlLoadAndStoreProperties = new LinkedHashMap<>();
htmlLoadAndStoreProperties.put("FilterName", "HTML (StarCalc)");
htmlLoadAndStoreProperties.put("FilterOptions", "utf8");
html.setStoreProperties(DocumentFamily.SPREADSHEET, htmlLoadAndStoreProperties);
html.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "impress_html_Export"));
addFormat(html);
DocumentFormat odt = new DocumentFormat("OpenDocument Text", "odt", "application/vnd.oasis.opendocument.text");
odt.setInputFamily(DocumentFamily.TEXT);
odt.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "writer8"));
addFormat(odt);
DocumentFormat sxw = new DocumentFormat("OpenOffice.org 1.0 Text Document", "sxw", "application/vnd.sun.xml.writer");
sxw.setInputFamily(DocumentFamily.TEXT);
sxw.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "StarOffice XML (Writer)"));
addFormat(sxw);
DocumentFormat doc = new DocumentFormat("Microsoft Word", "doc", "application/msword");
doc.setInputFamily(DocumentFamily.TEXT);
doc.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "MS Word 97"));
addFormat(doc);
DocumentFormat docx = new DocumentFormat("Microsoft Word 2007 XML", "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
docx.setInputFamily(DocumentFamily.TEXT);
addFormat(docx);
DocumentFormat rtf = new DocumentFormat("Rich Text Format", "rtf", "text/rtf");
rtf.setInputFamily(DocumentFamily.TEXT);
rtf.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "Rich Text Format"));
addFormat(rtf);
DocumentFormat wpd = new DocumentFormat("WordPerfect", "wpd", "application/wordperfect");
wpd.setInputFamily(DocumentFamily.TEXT);
addFormat(wpd);
DocumentFormat txt = new DocumentFormat("Plain Text", "txt", "text/plain");
txt.setInputFamily(DocumentFamily.TEXT);
Map<String,Object> txtLoadAndStoreProperties = new LinkedHashMap<>();
txtLoadAndStoreProperties.put("FilterName", "Text (encoded)");
txtLoadAndStoreProperties.put("FilterOptions", "utf8");
txt.setLoadProperties(txtLoadAndStoreProperties);
txt.setStoreProperties(DocumentFamily.TEXT, txtLoadAndStoreProperties);
addFormat(txt);
DocumentFormat wikitext = new DocumentFormat("MediaWiki wikitext", "wiki", "text/x-wiki");
wikitext.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "MediaWiki"));
//addFormat(wikitext);
DocumentFormat ods = new DocumentFormat("OpenDocument Spreadsheet", "ods", "application/vnd.oasis.opendocument.spreadsheet");
ods.setInputFamily(DocumentFamily.SPREADSHEET);
ods.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "calc8"));
addFormat(ods);
DocumentFormat sxc = new DocumentFormat("OpenOffice.org 1.0 Spreadsheet", "sxc", "application/vnd.sun.xml.calc");
sxc.setInputFamily(DocumentFamily.SPREADSHEET);
sxc.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "StarOffice XML (Calc)"));
addFormat(sxc);
DocumentFormat xls = new DocumentFormat("Microsoft Excel", "xls", "application/vnd.ms-excel");
xls.setInputFamily(DocumentFamily.SPREADSHEET);
xls.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "MS Excel 97"));
addFormat(xls);
DocumentFormat xlsx = new DocumentFormat("Microsoft Excel 2007 XML", "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
xlsx.setInputFamily(DocumentFamily.SPREADSHEET);
addFormat(xlsx);
DocumentFormat csv = new DocumentFormat("Comma Separated Values", "csv", "text/csv");
csv.setInputFamily(DocumentFamily.SPREADSHEET);
Map<String,Object> csvLoadAndStoreProperties = new LinkedHashMap<>();
csvLoadAndStoreProperties.put("FilterName", "Text - txt - csv (StarCalc)");
csvLoadAndStoreProperties.put("FilterOptions", "44,34,0"); // Field Separator: ','; Text Delimiter: '"'
csv.setLoadProperties(csvLoadAndStoreProperties);
csv.setStoreProperties(DocumentFamily.SPREADSHEET, csvLoadAndStoreProperties);
addFormat(csv);
DocumentFormat tsv = new DocumentFormat("Tab Separated Values", "tsv", "text/tab-separated-values");
tsv.setInputFamily(DocumentFamily.SPREADSHEET);
Map<String,Object> tsvLoadAndStoreProperties = new LinkedHashMap<>();
tsvLoadAndStoreProperties.put("FilterName", "Text - txt - csv (StarCalc)");
tsvLoadAndStoreProperties.put("FilterOptions", "9,34,0"); // Field Separator: '\t'; Text Delimiter: '"'
tsv.setLoadProperties(tsvLoadAndStoreProperties);
tsv.setStoreProperties(DocumentFamily.SPREADSHEET, tsvLoadAndStoreProperties);
addFormat(tsv);
DocumentFormat odp = new DocumentFormat("OpenDocument Presentation", "odp", "application/vnd.oasis.opendocument.presentation");
odp.setInputFamily(DocumentFamily.PRESENTATION);
odp.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "impress8"));
addFormat(odp);
DocumentFormat sxi = new DocumentFormat("OpenOffice.org 1.0 Presentation", "sxi", "application/vnd.sun.xml.impress");
sxi.setInputFamily(DocumentFamily.PRESENTATION);
sxi.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "StarOffice XML (Impress)"));
addFormat(sxi);
DocumentFormat ppt = new DocumentFormat("Microsoft PowerPoint", "ppt", "application/vnd.ms-powerpoint");
ppt.setInputFamily(DocumentFamily.PRESENTATION);
ppt.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "MS PowerPoint 97"));
addFormat(ppt);
DocumentFormat pptx = new DocumentFormat("Microsoft PowerPoint 2007 XML", "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
pptx.setInputFamily(DocumentFamily.PRESENTATION);
addFormat(pptx);
DocumentFormat odg = new DocumentFormat("OpenDocument Drawing", "odg", "application/vnd.oasis.opendocument.graphics");
odg.setInputFamily(DocumentFamily.DRAWING);
odg.setStoreProperties(DocumentFamily.DRAWING, Collections.singletonMap("FilterName", "draw8"));
addFormat(odg);
DocumentFormat svg = new DocumentFormat("Scalable Vector Graphics", "svg", "image/svg+xml");
svg.setStoreProperties(DocumentFamily.DRAWING, Collections.singletonMap("FilterName", "draw_svg_Export"));
addFormat(svg);
}
}

View File

@@ -0,0 +1,388 @@
package cn.keking.hutool;
import java.awt.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* 十六进制简写为hex或下标16在数学中是一种逢16进1的进位制一般用数字0到9和字母A到F表示其中:A~F即10~15。<br>
* 例如十进制数57在二进制写作111001在16进制写作39。<br>
* 像java,c这样的语言为了区分十六进制和十进制数值,会在十六进制数的前面加上 0x,比如0x20是十进制的32,而不是十进制的20<br>
* <p>
* 参考https://my.oschina.net/xinxingegeya/blog/287476
*
* @author Looly
*/
public class HexUtil {
/**
* 用于建立十六进制字符的输出的小写字符数组
*/
private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* 用于建立十六进制字符的输出的大写字符数组
*/
private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* 判断给定字符串是否为16进制数<br>
* 如果是,需要使用对应数字类型对象的<code>decode</code>方法解码<br>
* 例如:{@code Integer.decode}方法解码int类型的16进制数字
*
* @param value 值
* @return 是否为16进制
*/
public static boolean isHexNumber(String value) {
final int index = (value.startsWith("-") ? 1 : 0);
if (value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index)) {
try {
Long.decode(value);
} catch (NumberFormatException e) {
return false;
}
return true;
} else {
return false;
}
}
// ---------------------------------------------------------------------------------------------------- encode
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data) {
return encodeHex(data, true);
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param str 字符串
* @param charset 编码
* @return 十六进制char[]
*/
public static char[] encodeHex(String str, Charset charset) {
return encodeHex(StrUtil.bytes(str, charset), true);
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @param toLowerCase <code>true</code> 传换成小写格式 <code>false</code> 传换成大写格式
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @return 十六进制String
*/
public static String encodeHexStr(byte[] data) {
return encodeHexStr(data, true);
}
/**
* 将字节数组转换为十六进制字符串,结果为小写
*
* @param data 被编码的字符串
* @param charset 编码
* @return 十六进制String
*/
public static String encodeHexStr(String data, Charset charset) {
return encodeHexStr(StrUtil.bytes(data, charset), true);
}
/**
* 将字节数组转换为十六进制字符串结果为小写默认编码是UTF-8
*
* @param data 被编码的字符串
* @return 十六进制String
*/
public static String encodeHexStr(String data) {
return encodeHexStr(data, StandardCharsets.UTF_8);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @param toLowerCase <code>true</code> 传换成小写格式 <code>false</code> 传换成大写格式
* @return 十六进制String
*/
public static String encodeHexStr(byte[] data, boolean toLowerCase) {
return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
// ---------------------------------------------------------------------------------------------------- decode
/**
* 将十六进制字符数组转换为字符串默认编码UTF-8
*
* @param hexStr 十六进制String
* @return 字符串
*/
public static String decodeHexStr(String hexStr) {
return decodeHexStr(hexStr, StandardCharsets.UTF_8);
}
/**
* 将十六进制字符数组转换为字符串
*
* @param hexStr 十六进制String
* @param charset 编码
* @return 字符串
*/
public static String decodeHexStr(String hexStr, Charset charset) {
if (StrUtil.isEmpty(hexStr)) {
return hexStr;
}
return decodeHexStr(hexStr.toCharArray(), charset);
}
/**
* 将十六进制字符数组转换为字符串
*
* @param hexData 十六进制char[]
* @param charset 编码
* @return 字符串
*/
public static String decodeHexStr(char[] hexData, Charset charset) {
return StrUtil.str(decodeHex(hexData), charset);
}
/**
* 将十六进制字符数组转换为字节数组
*
* @param hexData 十六进制char[]
* @return byte[]
* @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常
*/
public static byte[] decodeHex(char[] hexData) {
int len = hexData.length;
if ((len & 0x01) != 0) {
throw new RuntimeException("Odd number of characters.");
}
byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(hexData[j], j) << 4;
j++;
f = f | toDigit(hexData[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
/**
* 将十六进制字符串解码为byte[]
*
* @param hexStr 十六进制String
* @return byte[]
*/
public static byte[] decodeHex(String hexStr) {
if (StrUtil.isEmpty(hexStr)) {
return null;
}
return decodeHex(hexStr.toCharArray());
}
// ---------------------------------------------------------------------------------------- Color
/**
* 将{@link Color}编码为Hex形式
*
* @param color {@link Color}
* @return Hex字符串
* @since 3.0.8
*/
public static String encodeColor(Color color) {
return encodeColor(color, "#");
}
/**
* 将{@link Color}编码为Hex形式
*
* @param color {@link Color}
* @param prefix 前缀字符串,可以是#、0x等
* @return Hex字符串
* @since 3.0.8
*/
public static String encodeColor(Color color, String prefix) {
final StringBuilder builder = new StringBuilder(prefix);
String colorHex;
colorHex = Integer.toHexString(color.getRed());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
colorHex = Integer.toHexString(color.getGreen());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
colorHex = Integer.toHexString(color.getBlue());
if (1 == colorHex.length()) {
builder.append('0');
}
builder.append(colorHex);
return builder.toString();
}
/**
* 将Hex颜色值转为
*
* @param hexColor 16进制颜色值可以以#开头也可以用0x开头
* @return {@link Color}
* @since 3.0.8
*/
public static Color decodeColor(String hexColor) {
return Color.decode(hexColor);
}
/**
* 将指定int值转换为Unicode字符串形式常用于特殊字符例如汉字转Unicode形式<br>
* 转换的字符串如果u后不足4位则前面用0填充例如
*
* <pre>
* '我' =》\u4f60
* </pre>
*
* @param value int值也可以是char
* @return Unicode表现形式
*/
public static String toUnicodeHex(int value) {
final StringBuilder builder = new StringBuilder(6);
builder.append("\\u");
String hex = toHex(value);
int len = hex.length();
if (len < 4) {
builder.append("0000", 0, 4 - len);// 不足4位补0
}
builder.append(hex);
return builder.toString();
}
/**
* 将指定char值转换为Unicode字符串形式常用于特殊字符例如汉字转Unicode形式<br>
* 转换的字符串如果u后不足4位则前面用0填充例如
*
* <pre>
* '我' =》\u4f60
* </pre>
*
* @param ch char值
* @return Unicode表现形式
* @since 4.0.1
*/
public static String toUnicodeHex(char ch) {
return "\\u" +//
DIGITS_LOWER[(ch >> 12) & 15] +//
DIGITS_LOWER[(ch >> 8) & 15] +//
DIGITS_LOWER[(ch >> 4) & 15] +//
DIGITS_LOWER[(ch) & 15];
}
/**
* 转为16进制字符串
*
* @param value int值
* @return 16进制字符串
* @since 4.4.1
*/
public static String toHex(int value) {
return Integer.toHexString(value);
}
/**
* 转为16进制字符串
*
* @param value int值
* @return 16进制字符串
* @since 4.4.1
*/
public static String toHex(long value) {
return Long.toHexString(value);
}
/**
* 将byte值转为16进制并添加到{@link StringBuilder}中
*
* @param builder {@link StringBuilder}
* @param b byte
* @param toLowerCase 是否使用小写
* @since 4.4.1
*/
public static void appendHex(StringBuilder builder, byte b, boolean toLowerCase) {
final char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER;
int high = (b & 0xf0) >>> 4;//高位
int low = b & 0x0f;//低位
builder.append(toDigits[high]);
builder.append(toDigits[low]);
}
// ---------------------------------------------------------------------------------------- Private method start
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @param toDigits 用于控制输出的char[]
* @return 十六进制String
*/
private static String encodeHexStr(byte[] data, char[] toDigits) {
return new String(encodeHex(data, toDigits));
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @param toDigits 用于控制输出的char[]
* @return 十六进制char[]
*/
private static char[] encodeHex(byte[] data, char[] toDigits) {
final int len = data.length;
final char[] out = new char[len << 1];//len*2
// two characters from the hex value.
for (int i = 0, j = 0; i < len; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];// 高位
out[j++] = toDigits[0x0F & data[i]];// 低位
}
return out;
}
/**
* 将十六进制字符转换成一个整数
*
* @param ch 十六进制char
* @param index 十六进制字符在字符数组中的位置
* @return 一个整数
* @throws RuntimeException 当ch不是一个合法的十六进制字符时抛出运行时异常
*/
private static int toDigit(char ch, int index) {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
}
return digit;
}
// ---------------------------------------------------------------------------------------- Private method end
}

View File

@@ -0,0 +1,283 @@
package cn.keking.hutool;
import java.nio.charset.Charset;
/**
* 字符串工具类
*
* @author xiaoleilu
*
*/
public class StrUtil {
public static final String EMPTY = "";
/**
* 是否空白符<br>
* 空白符包括空格、制表符、全角空格和不间断空格<br>
*
* @see Character#isWhitespace(int)
* @see Character#isSpaceChar(int)
* @param c 字符
* @return 是否空白符
* @since 4.0.10
*/
public static boolean isBlankChar(int c) {
return Character.isWhitespace(c) || Character.isSpaceChar(c) || c == '\ufeff' || c == '\u202a';
}
/**
* 是否空白符<br>
* 空白符包括空格、制表符、全角空格和不间断空格<br>
*
* @param c 字符
* @return 是否空白符
* @see Character#isWhitespace(int)
* @see Character#isSpaceChar(int)
* @since 4.0.10
*/
public static boolean isBlankChar(char c) {
return isBlankChar((int) c);
}
/**
* 字符串是否为空白 空白的定义如下: <br>
* 1、为null <br>
* 2、为不可见字符如空格<br>
* 3、""<br>
*
* @param str 被检测的字符串
* @return 是否为空
*/
public static boolean isBlank(CharSequence str) {
int length;
if ((str == null) || ((length = str.length()) == 0)) {
return true;
}
for (int i = 0; i < length; i++) {
// 只要有一个非空字符即为非空字符串
if (false == isBlankChar(str.charAt(i))) {
return false;
}
}
return true;
}
/**
* 字符串是否为空,空的定义如下:<br>
* 1、为null <br>
* 2、为""<br>
*
* @param str 被检测的字符串
* @return 是否为空
*/
public static boolean isEmpty(CharSequence str) {
return str == null || str.length() == 0;
}
/**
* 编码字符串
*
* @param str 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 编码后的字节码
*/
public static byte[] bytes(CharSequence str, Charset charset) {
if (str == null) {
return null;
}
if (null == charset) {
return str.toString().getBytes();
}
return str.toString().getBytes(charset);
}
/**
* {@link CharSequence} 转为字符串null安全
*
* @param cs {@link CharSequence}
* @return 字符串
*/
public static String str(CharSequence cs) {
return null == cs ? null : cs.toString();
}
/**
* 解码字节码
*
* @param data 字符串
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
* @return 解码后的字符串
*/
public static String str(byte[] data, Charset charset) {
if (data == null) {
return null;
}
if (null == charset) {
return new String(data);
}
return new String(data, charset);
}
/**
* 改进JDK subString<br>
* index从0开始计算最后一个字符为-1<br>
* 如果from和to位置一样返回 "" <br>
* 如果from或to为负数则按照length从后向前数位置如果绝对值大于字符串长度则from归到0to归到length<br>
* 如果经过修正的index中from大于to则互换from和to example: <br>
* abcdefgh 2 3 =》 c <br>
* abcdefgh 2 -3 =》 cde <br>
*
* @param str String
* @param fromIndex 开始的index包括
* @param toIndex 结束的index不包括
* @return 字串
*/
public static String sub(CharSequence str, int fromIndex, int toIndex) {
if (isEmpty(str)) {
return str(str);
}
int len = str.length();
if (fromIndex < 0) {
fromIndex = len + fromIndex;
if (fromIndex < 0) {
fromIndex = 0;
}
} else if (fromIndex > len) {
fromIndex = len;
}
if (toIndex < 0) {
toIndex = len + toIndex;
if (toIndex < 0) {
toIndex = len;
}
} else if (toIndex > len) {
toIndex = len;
}
if (toIndex < fromIndex) {
int tmp = fromIndex;
fromIndex = toIndex;
toIndex = tmp;
}
if (fromIndex == toIndex) {
return EMPTY;
}
return str.toString().substring(fromIndex, toIndex);
}
/**
* 切割指定位置之前部分的字符串
*
* @param string 字符串
* @param toIndex 切割到的位置(不包括)
* @return 切割后的剩余的前半部分字符串
*/
public static String subPre(CharSequence string, int toIndex) {
return sub(string, 0, toIndex);
}
/**
* 切割指定位置之后部分的字符串
*
* @param string 字符串
* @param fromIndex 切割开始的位置(包括)
* @return 切割后后剩余的后半部分字符串
*/
public static String subSuf(CharSequence string, int fromIndex) {
if (isEmpty(string)) {
return null;
}
return sub(string, fromIndex, string.length());
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @param start 起始位置如果小于0从0开始查找
* @param end 终止位置如果超过str.length()则默认查找到字符串末尾
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar, int start, int end) {
final int len = str.length();
if (start < 0 || start > len) {
start = 0;
}
if (end > len || end < 0) {
end = len;
}
for (int i = start; i < end; i++) {
if (str.charAt(i) == searchChar) {
return i;
}
}
return -1;
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @param start 起始位置如果小于0从0开始查找
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar, int start) {
if (str instanceof String) {
return ((String) str).indexOf(searchChar, start);
} else {
return indexOf(str, searchChar, start, -1);
}
}
/**
* 指定范围内查找指定字符
*
* @param str 字符串
* @param searchChar 被查找的字符
* @return 位置
*/
public static int indexOf(final CharSequence str, char searchChar) {
return indexOf(str, searchChar, 0);
}
/**
* 如果字符串是<code>null</code>,则返回指定默认字符串,否则返回字符串本身。
*
* <pre>
* nullToDefault(null, &quot;default&quot;) = &quot;default&quot;
* nullToDefault(&quot;&quot;, &quot;default&quot;) = &quot;&quot;
* nullToDefault(&quot; &quot;, &quot;default&quot;) = &quot; &quot;
* nullToDefault(&quot;bat&quot;, &quot;default&quot;) = &quot;bat&quot;
* </pre>
*
* @param str 要转换的字符串
* @param defaultStr 默认字符串
*
* @return 字符串本身或指定的默认字符串
*/
public static String nullToDefault(CharSequence str, String defaultStr) {
return (str == null) ? defaultStr : str.toString();
}
/**
* 当给定字符串为null时转换为Empty
*
* @param str 被转换的字符串
* @return 转换后的字符串
*/
public static String nullToEmpty(CharSequence str) {
return nullToDefault(str, EMPTY);
}
}

View File

@@ -0,0 +1,232 @@
package cn.keking.hutool;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.BitSet;
/**
* URL编码数据内容的类型是 application/x-www-form-urlencoded。
*
* <pre>
* 1.字符"a"-"z""A"-"Z""0"-"9"".""-""*",和"_" 都不会被编码;
* 2.将空格转换为%20 ;
* 3.将非文本内容转换成"%xy"的形式,xy是两位16进制的数值;
* 4.在每个 name=value 对之间放置 &amp; 符号。
* </pre>
*
* @author looly,
*
*/
public class URLEncoder implements Serializable{
private static final long serialVersionUID = 1L;
// --------------------------------------------------------------------------------------------- Static method start
/**
* 默认{@link URLEncoder}<br>
* 默认的编码器针对URI路径编码定义如下
*
* <pre>
* pchar = unreserved不处理 / pct-encoded / sub-delims子分隔符 / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&amp;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
* </pre>
*/
public static final URLEncoder DEFAULT = createDefault();
/**
* 用于查询语句的{@link URLEncoder}<br>
* 编码器针对URI路径编码定义如下
*
* <pre>
* 0x20 ' ' =》 '+'
* 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
* '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' Also '=' and '&amp;' 不编码
* 其它编码为 %nn 形式
* </pre>
*
* 详细见https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
*/
public static final URLEncoder QUERY = createQuery();
/**
* 创建默认{@link URLEncoder}<br>
* 默认的编码器针对URI路径编码定义如下
*
* <pre>
* pchar = unreserved不处理 / pct-encoded / sub-delims子分隔符 / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&amp;" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
* </pre>
*
* @return {@link URLEncoder}
*/
public static URLEncoder createDefault() {
final URLEncoder encoder = new URLEncoder();
encoder.addSafeCharacter('-');
encoder.addSafeCharacter('.');
encoder.addSafeCharacter('_');
encoder.addSafeCharacter('~');
// Add the sub-delims
encoder.addSafeCharacter('!');
encoder.addSafeCharacter('$');
encoder.addSafeCharacter('&');
encoder.addSafeCharacter('\'');
encoder.addSafeCharacter('(');
encoder.addSafeCharacter(')');
encoder.addSafeCharacter('*');
encoder.addSafeCharacter('+');
encoder.addSafeCharacter(',');
encoder.addSafeCharacter(';');
encoder.addSafeCharacter('=');
// Add the remaining literals
encoder.addSafeCharacter(':');
encoder.addSafeCharacter('@');
// Add '/' so it isn't encoded when we encode a path
encoder.addSafeCharacter('/');
return encoder;
}
/**
* 创建用于查询语句的{@link URLEncoder}<br>
* 编码器针对URI路径编码定义如下
*
* <pre>
* 0x20 ' ' =》 '+'
* 0x2A, 0x2D, 0x2E, 0x30 to 0x39, 0x41 to 0x5A, 0x5F, 0x61 to 0x7A as-is
* '*', '-', '.', '0' to '9', 'A' to 'Z', '_', 'a' to 'z' Also '=' and '&amp;' 不编码
* 其它编码为 %nn 形式
* </pre>
*
* 详细见https://www.w3.org/TR/html5/forms.html#application/x-www-form-urlencoded-encoding-algorithm
*
* @return {@link URLEncoder}
*/
public static URLEncoder createQuery() {
final URLEncoder encoder = new URLEncoder();
// Special encoding for space
encoder.setEncodeSpaceAsPlus(true);
// Alpha and digit are safe by default
// Add the other permitted characters
encoder.addSafeCharacter('*');
encoder.addSafeCharacter('-');
encoder.addSafeCharacter('.');
encoder.addSafeCharacter('_');
encoder.addSafeCharacter('=');
encoder.addSafeCharacter('&');
return encoder;
}
// --------------------------------------------------------------------------------------------- Static method end
/** 存放安全编码 */
private final BitSet safeCharacters;
/** 是否编码空格为+ */
private boolean encodeSpaceAsPlus = false;
/**
* 构造<br>
*
* [a-zA-Z0-9]默认不被编码
*/
public URLEncoder() {
this(new BitSet(256));
for (char i = 'a'; i <= 'z'; i++) {
addSafeCharacter(i);
}
for (char i = 'A'; i <= 'Z'; i++) {
addSafeCharacter(i);
}
for (char i = '0'; i <= '9'; i++) {
addSafeCharacter(i);
}
}
/**
* 构造
*
* @param safeCharacters 安全字符,安全字符不被编码
*/
private URLEncoder(BitSet safeCharacters) {
this.safeCharacters = safeCharacters;
}
/**
* 增加安全字符<br>
* 安全字符不被编码
*
* @param c 字符
*/
public void addSafeCharacter(char c) {
safeCharacters.set(c);
}
/**
* 移除安全字符<br>
* 安全字符不被编码
*
* @param c 字符
*/
public void removeSafeCharacter(char c) {
safeCharacters.clear(c);
}
/**
* 是否将空格编码为+
*
* @param encodeSpaceAsPlus 是否将空格编码为+
*/
public void setEncodeSpaceAsPlus(boolean encodeSpaceAsPlus) {
this.encodeSpaceAsPlus = encodeSpaceAsPlus;
}
/**
* 将URL中的字符串编码为%形式
*
* @param path 需要编码的字符串
* @param charset 编码
*
* @return 编码后的字符串
*/
public String encode(String path, Charset charset) {
int maxBytesPerChar = 10;
final StringBuilder rewrittenPath = new StringBuilder(path.length());
ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
OutputStreamWriter writer = new OutputStreamWriter(buf, charset);
int c;
for (int i = 0; i < path.length(); i++) {
c = path.charAt(i);
if (safeCharacters.get(c)) {
rewrittenPath.append((char) c);
} else if (encodeSpaceAsPlus && c == ' ') {
// 对于空格单独处理
rewrittenPath.append('+');
} else {
// convert to external encoding before hex conversion
try {
writer.write((char) c);
writer.flush();
} catch (IOException e) {
buf.reset();
continue;
}
byte[] ba = buf.toByteArray();
for (int j = 0; j < ba.length; j++) {
// Converting each byte in the buffer
byte toEncode = ba[j];
rewrittenPath.append('%');
HexUtil.appendHex(rewrittenPath, toEncode, false);
}
buf.reset();
}
}
return rewrittenPath.toString();
}
}

View File

@@ -0,0 +1,74 @@
package cn.keking.hutool;
import java.nio.charset.StandardCharsets;
/**
* 统一资源定位符相关工具类
*
* @author xiaoleilu
*
*/
public class URLUtil {
/**
* 标准化URL字符串包括
*
* <pre>
* 1. 多个/替换为一个
* </pre>
*
* @param url URL字符串
* @return 标准化后的URL字符串
*/
public static String normalize(String url) {
return normalize(url, false, false);
}
/**
* 标准化URL字符串包括
*
* <pre>
* 1. 多个/替换为一个
* </pre>
*
* @param url URL字符串
* @param isEncodeBody 是否对URL中body部分的中文和特殊字符做转义不包括http:和/
* @param isEncodeParam 是否对URL中参数部分的中文和特殊字符做转义
* @return 标准化后的URL字符串
* @since 4.4.1
*/
public static String normalize(String url, boolean isEncodeBody, boolean isEncodeParam) {
if (StrUtil.isBlank(url)) {
return url;
}
final int sepIndex = url.indexOf("://");
String pre;
String body;
if (sepIndex > 0) {
pre = StrUtil.subPre(url, sepIndex + 3);
body = StrUtil.subSuf(url, sepIndex + 3);
} else {
pre = "http://";
body = url;
}
final int paramsSepIndex = StrUtil.indexOf(body, '?');
String params = null;
if (paramsSepIndex > 0) {
params = StrUtil.subSuf(body, paramsSepIndex + 1);
body = StrUtil.subPre(body, paramsSepIndex);
}
// 去除开头的\或者/
body = body.replaceAll("^[\\\\/]+", StrUtil.EMPTY);
// 替换多个\或/为单个/
body = body.replace("\\", "/").replaceAll("//+", "/");
if (isEncodeBody) {
body = URLEncoder.DEFAULT.encode(body, StandardCharsets.UTF_8);
if (params != null) {
params = "?" + URLEncoder.DEFAULT.encode(params, StandardCharsets.UTF_8);
}
}
return pre + body + StrUtil.nullToEmpty(params);
}
}

View File

@@ -0,0 +1,83 @@
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 String fileKey;
private String officePreviewType = ConfigConstants.getOfficePreviewType();
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 String getFileKey() {
return fileKey;
}
public void setFileKey(String fileKey) {
this.fileKey = fileKey;
}
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 getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@@ -0,0 +1,67 @@
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"),
other("otherFilePreviewImpl"),
media("mediaFilePreviewImpl"),
markdown("markdownFilePreviewImpl"),
xml("xmlFilePreviewImpl"),
cad("cadFilePreviewImpl");
private static final String[] OFFICE_TYPES = {"docx", "doc", "xls", "xlsx", "ppt", "pptx"};
private static final String[] PICTURE_TYPES = {"jpg", "jpeg", "png", "gif", "bmp", "ico", "RAW"};
private static final String[] ARCHIVE_TYPES = {"rar", "zip", "jar", "7-zip", "tar", "gzip", "7z"};
private static final String[] SIMTEXT_TYPES = ConfigConstants.getSimText();
private static final String[] MEDIA_TYPES = ConfigConstants.getMedia();
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 : SIMTEXT_TYPES) {
FILE_TYPE_MAPPER.put(text, FileType.simText);
}
for (String media : MEDIA_TYPES) {
FILE_TYPE_MAPPER.put(media, FileType.media);
}
FILE_TYPE_MAPPER.put("md", FileType.markdown);
FILE_TYPE_MAPPER.put("xml", FileType.xml);
FILE_TYPE_MAPPER.put("pdf", FileType.pdf);
FILE_TYPE_MAPPER.put("dwg", FileType.cad);
}
public static FileType to(String fileType){
return FILE_TYPE_MAPPER.getOrDefault(fileType,other);
}
private final String instanceName;
FileType(String instanceName) {
this.instanceName = instanceName;
}
public String getInstanceName() {
return instanceName;
}
}

View File

@@ -0,0 +1,57 @@
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;
/**
* 返回状态
* 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 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,115 @@
package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.config.WatermarkConfigConstants;
import org.artofsolving.jodconverter.office.OfficeUtils;
import org.artofsolving.jodconverter.util.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;
/**
* @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 pdfDownloadDisable;
while (true) {
FileReader fileReader = new FileReader(configFilePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
properties.load(bufferedReader);
OfficeUtils.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_TYPE);
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_BASE_URL);
trustHost = properties.getProperty("trust.host", ConfigConstants.DEFAULT_TRUST_HOST);
pdfDownloadDisable = properties.getProperty("pdf.download.disable", ConfigConstants.DEFAULT_PDF_DOWNLOAD_DISABLE);
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.setOfficePreviewSwitchDisabledValue(officePreviewSwitchDisabled);
ConfigConstants.setPdfDownloadDisableValue(pdfDownloadDisable);
setWatermarkConfig(properties);
bufferedReader.close();
fileReader.close();
Thread.sleep(1000L);
}
} 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,83 @@
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.ExecutorService;
import java.util.concurrent.Executors;
/**
* 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 FilePreviewCommonService filePreviewCommonService;
public FileConvertQueueTask(FilePreviewFactory previewFactory, CacheService cacheService, FilePreviewCommonService filePreviewCommonService) {
this.previewFactory = previewFactory;
this.cacheService = cacheService;
this.filePreviewCommonService = filePreviewCommonService;
}
@PostConstruct
public void startTask(){
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.submit(new ConvertTask(previewFactory, cacheService, filePreviewCommonService));
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 FilePreviewCommonService filePreviewCommonService;
public ConvertTask(FilePreviewFactory previewFactory,
CacheService cacheService,
FilePreviewCommonService filePreviewCommonService) {
this.previewFactory = previewFactory;
this.cacheService = cacheService;
this.filePreviewCommonService = filePreviewCommonService;
}
@Override
public void run() {
while (true) {
String url = null;
try {
url = cacheService.takeQueueTask();
if(url != null){
FileAttribute fileAttribute = filePreviewCommonService.getFileAttribute(url,null);
FileType fileType = fileAttribute.getType();
logger.info("正在处理预览转换任务url{},预览类型:{}", url, fileType);
if(fileType.equals(FileType.compress) || fileType.equals(FileType.office) || fileType.equals(FileType.cad)) {
FilePreview filePreview = previewFactory.get(fileAttribute);
filePreview.filePreviewHandle(url, new ExtendedModelMap(), fileAttribute);
} else {
logger.info("预览类型无需处理url{},预览类型:{}", url, fileType);
}
}
} catch (Exception e) {
try {
Thread.sleep(1000*10);
} catch (Exception ex){
ex.printStackTrace();
}
logger.info("处理预览转换任务异常url{}", url, e);
}
}
}
}
}

View File

@@ -0,0 +1,17 @@
package cn.keking.service;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileAttribute;
import org.springframework.ui.Model;
/**
* Created by kl on 2018/1/17.
* Content :
*/
public interface FilePreview {
String TEXT_TYPE = "textType";
String DEFAULT_TEXT_TYPE = "simText";
String filePreviewHandle(String url, Model model, FileAttribute fileAttribute);
}

View File

@@ -0,0 +1,303 @@
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 org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @author yudian-it
* @date 2017/11/13
*/
@Component
public class FilePreviewCommonService {
private static final String DEFAULT_CONVERTER_CHARSET = System.getProperty("sun.jnu.encoding");
private final String fileDir = ConfigConstants.getFileDir();
private final CacheService cacheService;
public FilePreviewCommonService(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 getConvertedPdfImage(String key) {
return cacheService.getPdfImageCache(key);
}
/**
* 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型)
*
* @param url url
* @return 文件类型
*/
public FileType typeFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return this.typeFromFileName(fileName);
}
private FileType typeFromFileName(String fileName) {
String fileType = fileName.substring(fileName.lastIndexOf(".") + 1);
return FileType.to(fileType);
}
/**
* 从url中剥离出文件名
*
* @param url 格式如http://www.com.cn/20171113164107_月度绩效表模板(新).xls?UCloudPublicKey=ucloudtangshd@weifenf.com14355492830001993909323&Expires=&Signature=I D1NOFtAJSPT16E6imv6JWuq0k=
* @return 文件名
*/
public String getFileNameFromURL(String url) {
// 因为url的参数中可能会存在/的情况所以直接url.lastIndexOf("/")会有问题
// 所以先从处将url截断然后运用url.lastIndexOf("/")获取文件名
String noQueryUrl = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
return noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1);
}
/**
* 从路径中获取文件负
*
* @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 addConvertedPdfImage(String pdfFilePath, int num) {
cacheService.putPdfImageCache(pdfFilePath, num);
}
/**
* 获取redis中压缩包内图片文件
*
* @param fileKey fileKey
* @return 图片文件访问url列表
*/
public List<String> getImgCache(String fileKey) {
return cacheService.getImgCache(fileKey);
}
/**
* 设置redis中压缩包内图片文件
*
* @param fileKey fileKey
* @param imgs 图片文件访问url列表
*/
public void putImgCache(String fileKey, List<String> imgs) {
cacheService.putImgCache(fileKey, imgs);
}
/**
* 判断文件编码格式
*
* @param path 绝对路径
* @return 编码格式
*/
public String getFileEncodeUTFGBK(String path) {
String enc = Charset.forName("GBK").name();
File file = new File(path);
InputStream in;
try {
in = new FileInputStream(file);
byte[] b = new byte[3];
in.read(b);
in.close();
if (b[0] == -17 && b[1] == -69 && b[2] == -65) {
enc = StandardCharsets.UTF_8.name();
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("文件编码格式为:" + enc);
return enc;
}
/**
* 对转换后的文件进行操作(改变编码方式)
*
* @param outFilePath 文件绝对路径
*/
public void doActionConvertedFile(String outFilePath) {
StringBuilder sb = new StringBuilder();
try (InputStream inputStream = new FileInputStream(outFilePath);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, DEFAULT_CONVERTER_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.0.0.min.js\" type=\"text/javascript\"></script>");
sb.append("<script src=\"js/excel.header.js\" type=\"text/javascript\"></script>");
sb.append("<link rel=\"stylesheet\" href=\"bootstrap/css/bootstrap.min.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();
}
}
/**
* 获取文件后缀
*
* @param url url
* @return 文件后缀
*/
private String suffixFromUrl(String url) {
String nonPramStr = url.substring(0, url.contains("?") ? url.indexOf("?") : url.length());
String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1);
return suffixFromFileName(fileName);
}
private String suffixFromFileName(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
/**
* 获取url中的参数
*
* @param url url
* @param name 参数名
* @return 参数值
*/
public 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 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;
}
/**
* 获取文件属性
*
* @param url url
* @return 文件属性
*/
public FileAttribute getFileAttribute(String url, HttpServletRequest req) {
FileAttribute attribute = new FileAttribute();
String suffix;
FileType type;
String fileName;
String fullFileName = this.getUrlParameterReg(url, "fullfilename");
if (StringUtils.hasText(fullFileName)) {
fileName = fullFileName;
type = this.typeFromFileName(fullFileName);
suffix = suffixFromFileName(fullFileName);
} else {
fileName = getFileNameFromURL(url);
type = typeFromUrl(url);
suffix = suffixFromUrl(url);
}
attribute.setType(type);
attribute.setName(fileName);
attribute.setSuffix(suffix);
attribute.setUrl(url);
if (req != null) {
String officePreviewType = req.getParameter("officePreviewType");
String fileKey = req.getParameter("fileKey");
if(StringUtils.hasText(officePreviewType)){
attribute.setOfficePreviewType(officePreviewType);
}
if(StringUtils.hasText(fileKey)){
attribute.setFileKey(fileKey);
}
}
return attribute;
}
}

View File

@@ -0,0 +1,26 @@
package cn.keking.service;
import cn.keking.model.FileAttribute;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* 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) {
Map<String, FilePreview> filePreviewMap = context.getBeansOfType(FilePreview.class);
return filePreviewMap.get(fileAttribute.getType().getInstanceName());
}
}

View File

@@ -0,0 +1,134 @@
package cn.keking.service;
import com.sun.star.document.UpdateDocMode;
import cn.keking.extend.ControlDocumentFormatRegistry;
import org.apache.commons.lang3.StringUtils;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
import org.artofsolving.jodconverter.office.OfficeManager;
import org.artofsolving.jodconverter.office.OfficeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 创建文件转换器
*
* @author yudian-it
* @date 2017/11/13
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class OfficeProcessManager {
private final Logger logger = LoggerFactory.getLogger(OfficeProcessManager.class);
private OfficeManager officeManager;
@PostConstruct
public void initOfficeManager() {
new Thread(this::startOfficeManager).start();
}
/**
* 启动Office组件进程
*/
private void startOfficeManager(){
File officeHome = OfficeUtils.getDefaultOfficeHome();
if (officeHome == null) {
throw new RuntimeException("找不到office组件请确认'office.home'配置是否有误");
}
boolean killOffice = killProcess();
if (killOffice) {
logger.warn("检测到有正在运行的office进程已自动结束该进程");
}
try {
DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
configuration.setOfficeHome(officeHome);
configuration.setPortNumber(8100);
// 设置任务执行超时为5分钟
configuration.setTaskExecutionTimeout(1000 * 60 * 5L);
// 设置任务队列超时为24小时
//configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);
officeManager = configuration.buildOfficeManager();
officeManager.start();
} catch (Exception e) {
logger.error("启动office组件失败请检查office组件是否可用");
throw e;
}
}
public OfficeDocumentConverter getDocumentConverter() {
OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new ControlDocumentFormatRegistry());
converter.setDefaultLoadProperties(getLoadProperties());
return converter;
}
private Map<String,?> getLoadProperties() {
Map<String,Object> loadProperties = new HashMap<>(10);
loadProperties.put("Hidden", true);
loadProperties.put("ReadOnly", true);
loadProperties.put("UpdateDocMode", UpdateDocMode.QUIET_UPDATE);
loadProperties.put("CharacterSet", StandardCharsets.UTF_8.name());
return loadProperties;
}
private boolean killProcess() {
boolean flag = false;
Properties props = System.getProperties();
try {
if (props.getProperty("os.name").toLowerCase().contains("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 {
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;
}
}
} catch (IOException e) {
logger.error("检测office进程异常", e);
}
return flag;
}
@PreDestroy
public void destroyOfficeManager(){
if (null != officeManager && officeManager.isRunning()) {
officeManager.stop();
}
}
}

View File

@@ -0,0 +1,64 @@
package cn.keking.service;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.springframework.stereotype.Component;
import java.io.File;
/**
* @author yudian-it
*/
@Component
public class OfficeToPdfService {
private final OfficeProcessManager officeProcessManager;
public OfficeToPdfService(OfficeProcessManager officeProcessManager) {
this.officeProcessManager = officeProcessManager;
}
public void openOfficeToPDF(String inputFilePath, String outputFilePath) {
office2pdf(inputFilePath, outputFilePath);
}
public static void converterFile(File inputFile, String outputFilePath_end,
OfficeDocumentConverter converter) {
File outputFile = new File(outputFilePath_end);
// 假如目标路径不存在,则新建该路径
if (!outputFile.getParentFile().exists()) {
outputFile.getParentFile().mkdirs();
}
converter.convert(inputFile, outputFile);
}
public void office2pdf(String inputFilePath, String outputFilePath) {
OfficeDocumentConverter converter = officeProcessManager.getDocumentConverter();
if (null != inputFilePath) {
File inputFile = new File(inputFilePath);
// 判断目标文件路径是否为空
if (null == outputFilePath) {
// 转换后的文件路径
String outputFilePath_end = getOutputFilePath(inputFilePath);
if (inputFile.exists()) {
// 找不到源文件, 则返回
converterFile(inputFile, outputFilePath_end,converter);
}
} else {
if (inputFile.exists()) {
// 找不到源文件, 则返回
converterFile(inputFile, outputFilePath, converter);
}
}
}
}
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,36 @@
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 TASK_QUEUE_NAME = "convert-task";
Integer DEFAULT_PDF_CAPACITY = 500000;
Integer DEFAULT_IMG_CAPACITY = 500000;
Integer DEFAULT_PDFIMG_CAPACITY = 500000;
void initPDFCachePool(Integer capacity);
void initIMGCachePool(Integer capacity);
void initPdfImagesCachePool(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);
void cleanCache();
void addQueueTask(String url);
String takeQueueTask() throws InterruptedException;
}

View File

@@ -0,0 +1,119 @@
package cn.keking.service.cache.impl;
import cn.keking.service.cache.CacheService;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.Weighers;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
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 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);
}
@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 void cleanCache() {
initPDFCachePool(CacheService.DEFAULT_PDF_CAPACITY);
initIMGCachePool(CacheService.DEFAULT_IMG_CAPACITY);
initPdfImagesCachePool(CacheService.DEFAULT_PDFIMG_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();
}
}

View File

@@ -0,0 +1,124 @@
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 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 void cleanCache() {
cleanPdfCache();
cleanImgCache();
cleanPdfImgCache();
}
@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();
}
}

View File

@@ -0,0 +1,246 @@
package cn.keking.service.cache.impl;
import cn.keking.service.cache.CacheService;
import org.artofsolving.jodconverter.util.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 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
public void cleanCache() {
try {
cleanPdfCache();
cleanImgCache();
cleanPdfImgCache();
} 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));
}
}

View File

@@ -0,0 +1,89 @@
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.CadUtils;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService;
import cn.keking.utils.PdfUtils;
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 final FilePreviewCommonService filePreviewCommonService;
private final DownloadUtils downloadUtils;
private final CadUtils cadUtils;
private final PdfUtils pdfUtils;
public CadFilePreviewImpl(FilePreviewCommonService filePreviewCommonService,
DownloadUtils downloadUtils,
CadUtils cadUtils,
PdfUtils pdfUtils) {
this.filePreviewCommonService = filePreviewCommonService;
this.downloadUtils = downloadUtils;
this.cadUtils = cadUtils;
this.pdfUtils = pdfUtils;
}
private static final String OFFICE_PREVIEW_TYPE_IMAGE = "image";
private static final String OFFICE_PREVIEW_TYPE_ALL_IMAGES = "allImages";
private static final String FILE_DIR = ConfigConstants.getFileDir();
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 预览Type参数传了就取参数的没传取系统默认
String officePreviewType = model.asMap().get("officePreviewType") == null ? ConfigConstants.getOfficePreviewType() : model.asMap().get("officePreviewType").toString();
String baseUrl = BaseUrlFilter.getBaseUrl();
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "pdf";
String outFilePath = FILE_DIR + pdfName;
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (!filePreviewCommonService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
String filePath;
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) {
boolean convertResult = cadUtils.cadToPdf(filePath, outFilePath);
if (!convertResult) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", "cad文件转换异常请联系管理员");
return "fileNotSupported";
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
filePreviewCommonService.addConvertedFile(pdfName, filePreviewCommonService.getRelativePath(outFilePath));
}
}
}
if (baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) {
return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, pdfUtils, OFFICE_PREVIEW_TYPE_IMAGE);
}
model.addAttribute("pdfUrl", pdfName);
return "pdf";
}
}

View File

@@ -0,0 +1,71 @@
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.FilePreviewCommonService;
import cn.keking.utils.ZipReader;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
/**
* Created by kl on 2018/1/17.
* Content :处理压缩包文件
*/
@Service
public class CompressFilePreviewImpl implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService;
private final DownloadUtils downloadUtils;
private final ZipReader zipReader;
public CompressFilePreviewImpl(FilePreviewCommonService filePreviewCommonService,
DownloadUtils downloadUtils,
ZipReader zipReader) {
this.filePreviewCommonService = filePreviewCommonService;
this.downloadUtils = downloadUtils;
this.zipReader = zipReader;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName=fileAttribute.getName();
String suffix=fileAttribute.getSuffix();
String fileTree = null;
// 判断文件名是否存在(redis缓存读取)
if (!StringUtils.hasText(filePreviewCommonService.getConvertedFile(fileName)) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
String filePath = response.getContent();
if ("zip".equalsIgnoreCase(suffix) || "jar".equalsIgnoreCase(suffix) || "gzip".equalsIgnoreCase(suffix)) {
fileTree = zipReader.readZipFile(filePath, fileName);
} else if ("rar".equalsIgnoreCase(suffix)) {
fileTree = zipReader.unRar(filePath, fileName);
} else if ("7z".equalsIgnoreCase(suffix)) {
fileTree = zipReader.read7zFile(filePath, fileName);
}
if (fileTree != null && !"null".equals(fileTree) && ConfigConstants.isCacheEnabled()) {
filePreviewCommonService.addConvertedFile(fileName, fileTree);
}
} else {
fileTree = filePreviewCommonService.getConvertedFile(fileName);
}
if (fileTree != null && !"null".equals(fileTree)) {
model.addAttribute("fileTree", fileTree);
return "compress";
} else {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", "压缩文件类型不受支持尝试在压缩的时候选择RAR4格式");
return "fileNotSupported";
}
}
}

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) {
model.addAttribute(TEXT_TYPE,"markdown");
return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
}
}

View File

@@ -0,0 +1,54 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
/**
* @author : kl
* @authorboke : kailing.pub
* @create : 2018-03-25 上午11:58
* @description:
**/
@Service
public class MediaFilePreviewImpl implements FilePreview {
private final DownloadUtils downloadUtils;
private final FilePreviewCommonService filePreviewCommonService;
public MediaFilePreviewImpl(DownloadUtils downloadUtils,
FilePreviewCommonService filePreviewCommonService) {
this.downloadUtils = downloadUtils;
this.filePreviewCommonService = filePreviewCommonService;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileAttribute.getName());
if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} else {
model.addAttribute("mediaUrl", BaseUrlFilter.getBaseUrl() + filePreviewCommonService.getRelativePath(response.getContent()));
}
} else {
model.addAttribute("mediaUrl", url);
}
model.addAttribute("mediaUrl", url);
String suffix=fileAttribute.getSuffix();
if ("flv".equalsIgnoreCase(suffix)) {
return "flv";
}
return "media";
}
}

View File

@@ -0,0 +1,95 @@
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.FilePreviewCommonService;
import cn.keking.service.OfficeToPdfService;
import cn.keking.utils.PdfUtils;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* Created by kl on 2018/1/17.
* Content :处理office文件
*/
@Service
public class OfficeFilePreviewImpl implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService;
private final PdfUtils pdfUtils;
private final DownloadUtils downloadUtils;
private final OfficeToPdfService officeToPdfService;
public OfficeFilePreviewImpl(FilePreviewCommonService filePreviewCommonService, PdfUtils pdfUtils, DownloadUtils downloadUtils, OfficeToPdfService officeToPdfService) {
this.filePreviewCommonService = filePreviewCommonService;
this.pdfUtils = pdfUtils;
this.downloadUtils = downloadUtils;
this.officeToPdfService = officeToPdfService;
}
public static final String OFFICE_PREVIEW_TYPE_IMAGE = "image";
public static final String OFFICE_PREVIEW_TYPE_ALL_IMAGES = "allImages";
private static final String FILE_DIR = ConfigConstants.getFileDir();
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
// 预览Type参数传了就取参数的没传取系统默认
String officePreviewType = fileAttribute.getOfficePreviewType();
String baseUrl = BaseUrlFilter.getBaseUrl();
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
boolean isHtml = suffix.equalsIgnoreCase("xls") || suffix.equalsIgnoreCase("xlsx");
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf");
String outFilePath = FILE_DIR + pdfName;
// 判断之前是否已转换过,如果转换过,直接返回,否则执行转换
if (!filePreviewCommonService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
String filePath;
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
filePath = response.getContent();
if (StringUtils.hasText(outFilePath)) {
officeToPdfService.openOfficeToPDF(filePath, outFilePath);
if (isHtml) {
// 对转换后的文件进行操作(改变编码方式)
filePreviewCommonService.doActionConvertedFile(outFilePath);
}
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
filePreviewCommonService.addConvertedFile(pdfName, filePreviewCommonService.getRelativePath(outFilePath));
}
}
}
if (!isHtml && baseUrl != null && (OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType))) {
return getPreviewType(model, fileAttribute, officePreviewType, baseUrl, pdfName, outFilePath, pdfUtils, OFFICE_PREVIEW_TYPE_IMAGE);
}
model.addAttribute("pdfUrl", pdfName);
return isHtml ? "html" : "pdf";
}
static String getPreviewType(Model model, FileAttribute fileAttribute, String officePreviewType, String baseUrl, String pdfName, String outFilePath, PdfUtils pdfUtils, String officePreviewTypeImage) {
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) {
model.addAttribute("msg", "office转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (officePreviewTypeImage.equals(officePreviewType)) {
return "officePicture";
} else {
return "picture";
}
}
}

View File

@@ -0,0 +1,20 @@
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 OtherFilePreviewImpl implements FilePreview {
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute("fileType",fileAttribute.getSuffix());
model.addAttribute("msg", "系统还不支持该格式文件的在线预览");
return "fileNotSupported";
}
}

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.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService;
import cn.keking.utils.PdfUtils;
import cn.keking.web.filter.BaseUrlFilter;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import java.util.List;
/**
* Created by kl on 2018/1/17.
* Content :处理pdf文件
*/
@Service
public class PdfFilePreviewImpl implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService;
private final PdfUtils pdfUtils;
private final DownloadUtils downloadUtils;
private static final String FILE_DIR = ConfigConstants.getFileDir();
public PdfFilePreviewImpl(FilePreviewCommonService filePreviewCommonService,
PdfUtils pdfUtils,
DownloadUtils downloadUtils) {
this.filePreviewCommonService = filePreviewCommonService;
this.pdfUtils = pdfUtils;
this.downloadUtils = downloadUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String suffix=fileAttribute.getSuffix();
String fileName=fileAttribute.getName();
String officePreviewType = fileAttribute.getOfficePreviewType();
String baseUrl = BaseUrlFilter.getBaseUrl();
String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + "pdf";
String outFilePath = FILE_DIR + pdfName;
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType) || OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_ALL_IMAGES.equals(officePreviewType)) {
//当文件不存在时,就去下载
if (!filePreviewCommonService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
outFilePath = response.getContent();
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
filePreviewCommonService.addConvertedFile(pdfName, filePreviewCommonService.getRelativePath(outFilePath));
}
}
List<String> imageUrls = pdfUtils.pdf2jpg(outFilePath, pdfName, baseUrl);
if (imageUrls == null || imageUrls.size() < 1) {
model.addAttribute("msg", "pdf转图片异常请联系管理员");
model.addAttribute("fileType",fileAttribute.getSuffix());
return "fileNotSupported";
}
model.addAttribute("imgurls", imageUrls);
model.addAttribute("currentUrl", imageUrls.get(0));
if (OfficeFilePreviewImpl.OFFICE_PREVIEW_TYPE_IMAGE.equals(officePreviewType)) {
return "officePicture";
} else {
return "picture";
}
} else {
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
if (!filePreviewCommonService.listConvertedFiles().containsKey(pdfName) || !ConfigConstants.isCacheEnabled()) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, pdfName);
if (0 != response.getCode()) {
model.addAttribute("fileType", suffix);
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
}
model.addAttribute("pdfUrl", filePreviewCommonService.getRelativePath(response.getContent()));
if (ConfigConstants.isCacheEnabled()) {
// 加入缓存
filePreviewCommonService.addConvertedFile(pdfName, filePreviewCommonService.getRelativePath(outFilePath));
}
} else {
model.addAttribute("pdfUrl", pdfName);
}
} else {
model.addAttribute("pdfUrl", url);
}
}
return "pdf";
}
}

View File

@@ -0,0 +1,60 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService;
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 implements FilePreview {
private final FilePreviewCommonService filePreviewCommonService;
private final DownloadUtils downloadUtils;
public PictureFilePreviewImpl(FilePreviewCommonService filePreviewCommonService,
DownloadUtils downloadUtils) {
this.filePreviewCommonService = filePreviewCommonService;
this.downloadUtils = downloadUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
List<String> imgUrls = new ArrayList<>();
imgUrls.add(url);
String fileKey = fileAttribute.getFileKey();
List<String> zipImgUrls = filePreviewCommonService.getImgCache(fileKey);
if (!CollectionUtils.isEmpty(zipImgUrls)) {
imgUrls.addAll(zipImgUrls);
}
// 不是http开头浏览器不能直接访问需下载到本地
if (url != null && !url.toLowerCase().startsWith("http")) {
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, null);
if (0 != response.getCode()) {
model.addAttribute("fileType", fileAttribute.getSuffix());
model.addAttribute("msg", response.getMsg());
return "fileNotSupported";
} else {
String file = filePreviewCommonService.getRelativePath(response.getContent());
imgUrls.clear();
imgUrls.add(file);
model.addAttribute("imgurls", imgUrls);
model.addAttribute("currentUrl", file);
}
} else {
model.addAttribute("imgurls", imgUrls);
model.addAttribute("currentUrl", url);
}
return "picture";
}
}

View File

@@ -0,0 +1,56 @@
package cn.keking.service.impl;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreview;
import cn.keking.utils.DownloadUtils;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;
import org.springframework.util.Base64Utils;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
/**
* Created by kl on 2018/1/17.
* Content :处理文本文件
*/
@Service
public class SimTextFilePreviewImpl implements FilePreview {
private final DownloadUtils downloadUtils;
public SimTextFilePreviewImpl(DownloadUtils downloadUtils) {
this.downloadUtils = downloadUtils;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
String fileName = fileAttribute.getName();
ReturnResponse<String> response = downloadUtils.downLoad(fileAttribute, fileName);
if (0 != response.getCode()) {
model.addAttribute("msg", response.getMsg());
model.addAttribute("fileType", fileAttribute.getSuffix());
return "fileNotSupported";
}
try {
File originFile = new File(response.getContent());
String xmlString = FileUtils.readFileToString(originFile, StandardCharsets.UTF_8);
model.addAttribute("textData", Base64Utils.encodeToString(xmlString.getBytes()));
} catch (IOException e) {
model.addAttribute("msg", e.getLocalizedMessage());
model.addAttribute("fileType", fileAttribute.getSuffix());
return "fileNotSupported";
}
if (!model.containsAttribute(TEXT_TYPE)) {
model.addAttribute(TEXT_TYPE, DEFAULT_TEXT_TYPE);
}
return "txt";
}
}

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 XmlFilePreviewImpl implements FilePreview {
private final SimTextFilePreviewImpl simTextFilePreview;
public XmlFilePreviewImpl(SimTextFilePreviewImpl simTextFilePreview) {
this.simTextFilePreview = simTextFilePreview;
}
@Override
public String filePreviewHandle(String url, Model model, FileAttribute fileAttribute) {
model.addAttribute(TEXT_TYPE,"xml");
return simTextFilePreview.filePreviewHandle(url, model, fileAttribute);
}
}

View File

@@ -0,0 +1,50 @@
package cn.keking.utils;
import com.aspose.cad.Color;
import com.aspose.cad.fileformats.cad.CadDrawTypeMode;
import com.aspose.cad.imageoptions.CadRasterizationOptions;
import com.aspose.cad.imageoptions.PdfOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
/**
* @author chenjhc
* @since 2019/11/21 14:34
*/
@Component
public class CadUtils {
private final Logger logger = LoggerFactory.getLogger(CadUtils.class);
public boolean cadToPdf(String inputFilePath, String outputFilePath) {
com.aspose.cad.Image cadImage = com.aspose.cad.Image.load(inputFilePath);
CadRasterizationOptions cadRasterizationOptions = new CadRasterizationOptions();
cadRasterizationOptions.setLayouts(new String[]{"Model"});
cadRasterizationOptions.setNoScaling(true);
cadRasterizationOptions.setBackgroundColor(Color.getWhite());
cadRasterizationOptions.setPageWidth(cadImage.getWidth());
cadRasterizationOptions.setPageHeight(cadImage.getHeight());
cadRasterizationOptions.setPdfProductLocation("center");
cadRasterizationOptions.setAutomaticLayoutsScaling(true);
cadRasterizationOptions.setDrawType(CadDrawTypeMode.UseObjectColor);
PdfOptions pdfOptions = new PdfOptions();
pdfOptions.setVectorRasterizationOptions(cadRasterizationOptions);
File outputFile = new File(outputFilePath);
OutputStream stream;
try {
stream = new FileOutputStream(outputFile);
cadImage.save(stream, pdfOptions);
cadImage.close();
return true;
} catch (FileNotFoundException e) {
logger.error("PDFFileNotFoundExceptioninputFilePath{}", inputFilePath, e);
return false;
}
}
}

View File

@@ -0,0 +1,82 @@
package cn.keking.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Objects;
public class DeleteFileUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(DeleteFileUtil.class);
/**
* 删除单个文件
*
* @param fileName
* 要删除的文件的文件名
* @return 单个文件删除成功返回true否则返回false
*/
public static boolean deleteFile(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;
}
}
/**
* 删除目录及目录下的文件
*
* @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 = DeleteFileUtil.deleteFile(files[i].getAbsolutePath());
if (!flag) {
break;
}
} else if (files[i].isDirectory()) {
// 删除子目录
flag = DeleteFileUtil.deleteDirectory(files[i].getAbsolutePath());
if (!flag) {
break;
}
}
}
dirFile.delete();
if (!flag) {
LOGGER.info("删除目录失败!");
return false;
}
return true;
}
}

View File

@@ -0,0 +1,177 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.hutool.URLUtil;
import cn.keking.model.FileAttribute;
import cn.keking.model.FileType;
import cn.keking.model.ReturnResponse;
import cn.keking.service.FilePreviewCommonService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
/**
* @author yudian-it
*/
@Component
public class DownloadUtils {
private final Logger logger = LoggerFactory.getLogger(DownloadUtils.class);
private final String fileDir = ConfigConstants.getFileDir();
private final FilePreviewCommonService filePreviewCommonService;
public DownloadUtils(FilePreviewCommonService filePreviewCommonService) {
this.filePreviewCommonService = filePreviewCommonService;
}
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";
/**
* @param fileAttribute fileAttribute
* @param fileName 文件名
* @return 本地文件绝对路径
*/
public ReturnResponse<String> downLoad(FileAttribute fileAttribute, String fileName) {
String urlStr = fileAttribute.getUrl();
String type = fileAttribute.getSuffix();
ReturnResponse<String> response = new ReturnResponse<>(0, "下载成功!!!", "");
UUID uuid = UUID.randomUUID();
if (null == fileName) {
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();
}
try {
URL url = new URL(urlStr);
if (url.getProtocol() != null && (url.getProtocol().toLowerCase().startsWith("file")||url.getProtocol().toLowerCase().startsWith("http"))) {
byte[] bytes = getBytesFromUrl(urlStr);
OutputStream os = new FileOutputStream(realPath);
saveBytesToOutStream(bytes, os);
} else if (url.getProtocol() != null && "ftp".equalsIgnoreCase(url.getProtocol())) {
String ftpUsername = filePreviewCommonService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_USERNAME);
String ftpPassword = filePreviewCommonService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_PASSWORD);
String ftpControlEncoding = filePreviewCommonService.getUrlParameterReg(fileAttribute.getUrl(), URL_PARAM_FTP_CONTROL_ENCODING);
FtpUtils.download(fileAttribute.getUrl(), realPath, ftpUsername, ftpPassword, ftpControlEncoding);
} else {
response.setCode(1);
response.setContent(null);
response.setMsg("url不能识别url" + urlStr);
}
response.setContent(realPath);
response.setMsg(fileName);
if(FileType.simText.equals(fileAttribute.getType())){
convertTextPlainFileCharsetToUtf8(realPath);
}
return response;
} catch (IOException e) {
logger.error("文件下载失败url{}", urlStr, e);
response.setCode(1);
response.setContent(null);
if (e instanceof FileNotFoundException) {
response.setMsg("文件不存在!!!");
} else {
response.setMsg(e.getMessage());
}
return response;
}
}
public byte[] getBytesFromUrl(String urlStr) throws IOException {
InputStream is = getInputStreamFromUrl(urlStr);
if (is != null) {
return getBytesFromStream(is);
} else {
urlStr = URLUtil.normalize(urlStr, true, true);
is = getInputStreamFromUrl(urlStr);
if (is == null) {
logger.error("文件下载异常url{}", urlStr);
throw new IOException("文件下载异常url" + urlStr);
}
return getBytesFromStream(is);
}
}
public void saveBytesToOutStream(byte[] b, OutputStream os) throws IOException {
os.write(b);
os.close();
}
private InputStream getInputStreamFromUrl(String urlStr) {
try {
URL url = new URL(urlStr);
URLConnection connection = url.openConnection();
if (connection instanceof HttpURLConnection) {
connection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
}
return connection.getInputStream();
} catch (IOException e) {
logger.warn("连接url异常url{}", urlStr);
return null;
}
}
private byte[] getBytesFromStream(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
byte[] b = baos.toByteArray();
is.close();
baos.close();
return b;
}
/**
* 转换文本文件编码为utf8
* 探测源文件编码,探测到编码切不为utf8则进行转码
* @param filePath 文件路径
*/
private static void convertTextPlainFileCharsetToUtf8(String filePath) throws IOException {
File sourceFile = new File(filePath);
if(sourceFile.exists() && sourceFile.isFile() && sourceFile.canRead()) {
String encoding = null;
try {
FileCharsetDetector.Observer observer = FileCharsetDetector.guessFileEncoding(sourceFile);
// 为准确探测到编码,不适用猜测的编码
encoding = observer.isFound()?observer.getEncoding():null;
// 为准确探测到编码,可以考虑使用GBK 大部分文件都是windows系统产生的
} catch (IOException e) {
// 编码探测失败,
e.printStackTrace();
}
if(encoding != null && !"UTF-8".equals(encoding)){
// 不为utf8,进行转码
File tmpUtf8File = new File(filePath+".utf8");
Writer writer = new OutputStreamWriter(new FileOutputStream(tmpUtf8File), StandardCharsets.UTF_8);
Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile),encoding));
char[] buf = new char[1024];
int read;
while ((read = reader.read(buf)) > 0){
writer.write(buf, 0, read);
}
reader.close();
writer.close();
// 删除源文件
sourceFile.delete();
// 重命名
tmpUtf8File.renameTo(sourceFile);
}
}
}
}

View File

@@ -0,0 +1,157 @@
package cn.keking.utils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.mozilla.intl.chardet.nsDetector;
import org.mozilla.intl.chardet.nsICharsetDetectionObserver;
/**
* 文本文件编码探测工具类
*
* @author HWliao
* @date 2017-12-24
*/
public class FileCharsetDetector {
/**
* 传入一个文件(File)对象,检查文件编码
*
* @param file File对象实例
* @return 文件编码若无则返回null
* @throws FileNotFoundException
* @throws IOException
*/
public static Observer guessFileEncoding(File file)
throws FileNotFoundException, IOException {
return guessFileEncoding(file, new nsDetector());
}
/**
* <pre>
* 获取文件的编码
* @param file
* File对象实例
* @param languageHint
* 语言提示区域代码 @see #nsPSMDetector ,取值如下:
* 1 : Japanese
* 2 : Chinese
* 3 : Simplified Chinese
* 4 : Traditional Chinese
* 5 : Korean
* 6 : Dont know(default)
* </pre>
*
* @return 文件编码egUTF-8,GBK,GB2312形式(不确定的时候,返回可能的字符编码序列)若无则返回null
* @throws FileNotFoundException
* @throws IOException
*/
public static Observer guessFileEncoding(File file, int languageHint)
throws FileNotFoundException, IOException {
return guessFileEncoding(file, new nsDetector(languageHint));
}
/**
* 获取文件的编码
*
* @param file
* @param det
* @return
* @throws FileNotFoundException
* @throws IOException
*/
private static Observer guessFileEncoding(File file, nsDetector det)
throws FileNotFoundException, IOException {
// new Observer
Observer observer = new Observer();
// set Observer
// The Notify() will be called when a matching charset is found.
det.Init(observer);
BufferedInputStream imp = new BufferedInputStream(new FileInputStream(
file));
byte[] buf = new byte[1024];
int len;
boolean done = false;
boolean isAscii = false;
while ((len = imp.read(buf, 0, buf.length)) != -1) {
// Check if the stream is only ascii.
isAscii = det.isAscii(buf, len);
if (isAscii) {
break;
}
// DoIt if non-ascii and not done yet.
done = det.DoIt(buf, len, false);
if (done) {
break;
}
}
imp.close();
det.DataEnd();
if (isAscii) {
observer.encoding = "ASCII";
observer.found = true;
}
if (!observer.isFound()) {
String[] prob = det.getProbableCharsets();
// // 这里将可能的字符集组合起来返回
// for (int i = 0; i < prob.length; i++) {
// if (i == 0) {
// encoding = prob[i];
// } else {
// encoding += "," + prob[i];
// }
// }
if (prob.length > 0) {
// 在没有发现情况下,去第一个可能的编码
observer.encoding = prob[0];
} else {
observer.encoding = null;
}
}
return observer;
}
/**
* @author liaohongwei
* @Description: 文件字符编码观察者, 但判断出字符编码时候调用
* @date 2016年6月20日 下午2:27:06
*/
public static class Observer implements nsICharsetDetectionObserver {
/**
* @Fields encoding : 字符编码
*/
private String encoding = null;
/**
* @Fields found : 是否找到字符集
*/
private boolean found = false;
@Override
public void Notify(String charset) {
this.encoding = charset;
this.found = true;
}
public String getEncoding() {
return encoding;
}
public boolean isFound() {
return found;
}
@Override
public String toString() {
return "Observer [encoding=" + encoding + ", found=" + found + "]";
}
}
}

View File

@@ -0,0 +1,58 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
/**
* @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 = new FileOutputStream(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,79 @@
package cn.keking.utils;
import cn.keking.service.FilePreviewCommonService;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
@Component
public class PdfUtils {
private final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
private final FilePreviewCommonService filePreviewCommonService;
@Value("${server.tomcat.uri-encoding:UTF-8}")
private String uriEncoding;
public PdfUtils(FilePreviewCommonService filePreviewCommonService) {
this.filePreviewCommonService = filePreviewCommonService;
}
public List<String> pdf2jpg(String pdfFilePath, String pdfName, String baseUrl) {
List<String> imageUrls = new ArrayList<>();
Integer imageCount = filePreviewCommonService.getConvertedPdfImage(pdfFilePath);
String imageFileSuffix = ".jpg";
String pdfFolder = pdfName.substring(0, pdfName.length() - 4);
String urlPrefix = null;
try {
urlPrefix = baseUrl + URLEncoder.encode(URLEncoder.encode(pdfFolder, uriEncoding).replaceAll("\\+", "%20"), uriEncoding);
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException", e);
urlPrefix = baseUrl + pdfFolder;
}
if (imageCount != null && imageCount > 0) {
for (int i = 0; i < imageCount ; i++)
imageUrls.add(urlPrefix + "/" + i + imageFileSuffix);
return imageUrls;
}
try {
File pdfFile = new File(pdfFilePath);
PDDocument doc = PDDocument.load(pdfFile);
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();
}
String imageFilePath;
for (int pageIndex = 0; pageIndex < pageCount; pageIndex++) {
imageFilePath = folder + File.separator + pageIndex + imageFileSuffix;
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 105, ImageType.RGB);
ImageIOUtil.writeImage(image, imageFilePath, 105);
imageUrls.add(urlPrefix + "/" + pageIndex + imageFileSuffix);
}
doc.close();
filePreviewCommonService.addConvertedPdfImage(pdfFilePath, pageCount);
} catch (IOException e) {
logger.error("Convert pdf to jpg exception, pdfFilePath{}", pdfFilePath, e);
}
return imageUrls;
}
}

View File

@@ -0,0 +1,37 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.service.cache.CacheService;
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 ShedulerClean {
private final Logger logger = LoggerFactory.getLogger(ShedulerClean.class);
private final CacheService cacheService;
public ShedulerClean(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();
DeleteFileUtil.deleteDirectory(fileDir);
logger.info("Cache clean end");
}
}

View File

@@ -0,0 +1,490 @@
package cn.keking.utils;
import cn.keking.config.ConfigConstants;
import cn.keking.model.FileType;
import cn.keking.service.FilePreviewCommonService;
import cn.keking.web.filter.BaseUrlFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.junrar.Archive;
import com.github.junrar.exception.RarException;
import com.github.junrar.rarfile.FileHeader;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.springframework.stereotype.Component;
import java.io.*;
import java.math.BigDecimal;
import java.text.CollationKey;
import java.text.Collator;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author yudian-it
* @date 2017/11/27
*/
@Component
public class ZipReader {
static Pattern pattern = Pattern.compile("^\\d+");
private final FilePreviewCommonService filePreviewCommonService;
private final String fileDir = ConfigConstants.getFileDir();
private final ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public ZipReader(FilePreviewCommonService filePreviewCommonService) {
this.filePreviewCommonService = filePreviewCommonService;
}
public String readZipFile(String filePath,String fileKey) {
String archiveSeparator = "/";
Map<String, FileNode> appender = new HashMap<>();
List<String> imgUrls = new LinkedList<>();
String baseUrl = BaseUrlFilter.getBaseUrl();
String archiveFileName = filePreviewCommonService.getFileNameFromPath(filePath);
try {
ZipFile zipFile = new ZipFile(filePath, filePreviewCommonService.getFileEncodeUTFGBK(filePath));
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
// 排序
entries = sortZipEntries(entries);
List<Map<String, ZipArchiveEntry>> entriesToBeExtracted = new LinkedList<>();
while (entries.hasMoreElements()){
ZipArchiveEntry entry = entries.nextElement();
String fullName = entry.getName();
int level = fullName.split(archiveSeparator).length;
// 展示名
String originName = getLastFileName(fullName, archiveSeparator);
String childName = level + "_" + originName;
boolean directory = entry.isDirectory();
if (!directory) {
childName = archiveFileName + "_" + originName;
entriesToBeExtracted.add(Collections.singletonMap(childName, entry));
}
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level-1) + "_" + parentName;
FileType type= filePreviewCommonService.typeFromUrl(childName);
if (type.equals(FileType.picture)){//添加图片文件到图片列表
imgUrls.add(baseUrl+childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node);
appender.put(childName, node);
}
// 开启新的线程处理文件解压
executors.submit(new ZipExtractorWorker(entriesToBeExtracted, zipFile, filePath));
filePreviewCommonService.putImgCache(fileKey,imgUrls);
return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private Enumeration<ZipArchiveEntry> sortZipEntries(Enumeration<ZipArchiveEntry> entries) {
List<ZipArchiveEntry> sortedEntries = new LinkedList<>();
while(entries.hasMoreElements()){
sortedEntries.add(entries.nextElement());
}
sortedEntries.sort(Comparator.comparingInt(o -> o.getName().length()));
return Collections.enumeration(sortedEntries);
}
public String unRar(String filePath,String fileKey){
Map<String, FileNode> appender = new HashMap<>();
List<String> imgUrls = new ArrayList<>();
String baseUrl = BaseUrlFilter.getBaseUrl();
try {
Archive archive = new Archive(new FileInputStream(new File(filePath)));
List<FileHeader> headers = archive.getFileHeaders();
headers = sortedHeaders(headers);
String archiveFileName = filePreviewCommonService.getFileNameFromPath(filePath);
List<Map<String, FileHeader>> headersToBeExtracted =new ArrayList<>();
for (FileHeader header : headers) {
String fullName;
if (header.isUnicode()) {
fullName = header.getFileNameW();
}else {
fullName = header.getFileNameString();
}
// 展示名
String originName = getLastFileName(fullName, "\\");
String childName = originName;
boolean directory = header.isDirectory();
if (!directory) {
childName = archiveFileName + "_" + originName;
headersToBeExtracted.add(Collections.singletonMap(childName, header));
}
String parentName = getLast2FileName(fullName, "\\", archiveFileName);
FileType type = filePreviewCommonService.typeFromUrl(childName);
if (type.equals(FileType.picture)){//添加图片文件到图片列表
imgUrls.add(baseUrl+childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node);
appender.put(childName, node);
}
executors.submit(new RarExtractorWorker(headersToBeExtracted, archive, filePath));
filePreviewCommonService.putImgCache(fileKey,imgUrls);
return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (RarException | IOException e) {
e.printStackTrace();
}
return null;
}
public String read7zFile(String filePath,String fileKey) {
String archiveSeparator = "/";
Map<String, FileNode> appender = new HashMap<>();
List<String> imgUrls = new ArrayList<>();
String baseUrl= BaseUrlFilter.getBaseUrl();
String archiveFileName = filePreviewCommonService.getFileNameFromPath(filePath);
try {
SevenZFile zipFile = new SevenZFile(new File(filePath));
Iterable<SevenZArchiveEntry> entries = zipFile.getEntries();
// 排序
Enumeration<SevenZArchiveEntry> newEntries = sortSevenZEntries(entries);
List<Map<String, SevenZArchiveEntry>> entriesToBeExtracted = new ArrayList<>();
while (newEntries.hasMoreElements()){
SevenZArchiveEntry entry = newEntries.nextElement();
String fullName = entry.getName();
int level = fullName.split(archiveSeparator).length;
// 展示名
String originName = getLastFileName(fullName, archiveSeparator);
String childName = level + "_" + originName;
boolean directory = entry.isDirectory();
if (!directory) {
childName = archiveFileName + "_" + originName;
entriesToBeExtracted.add(Collections.singletonMap(childName, entry));
}
String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
parentName = (level-1) + "_" + parentName;
FileType type= filePreviewCommonService.typeFromUrl(childName);
if (type.equals(FileType.picture)){//添加图片文件到图片列表
imgUrls.add(baseUrl+childName);
}
FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory, fileKey);
addNodes(appender, parentName, node);
appender.put(childName, node);
}
// 开启新的线程处理文件解压
executors.submit(new SevenZExtractorWorker(entriesToBeExtracted, filePath));
filePreviewCommonService.putImgCache(fileKey,imgUrls);
return new ObjectMapper().writeValueAsString(appender.get(""));
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private Enumeration<SevenZArchiveEntry> sortSevenZEntries(Iterable<SevenZArchiveEntry> entries) {
List<SevenZArchiveEntry> sortedEntries = new ArrayList<>();
for (SevenZArchiveEntry entry : entries) {
sortedEntries.add(entry);
}
return Collections.enumeration(sortedEntries);
}
private void addNodes(Map<String, FileNode> appender, String parentName, FileNode node) {
if (appender.containsKey(parentName)) {
appender.get(parentName).getChildList().add(node);
appender.get(parentName).getChildList().sort(sortComparator);
} else {
// 根节点
FileNode nodeRoot = new FileNode(parentName, parentName, "", new ArrayList<>(), true);
nodeRoot.getChildList().add(node);
appender.put("", nodeRoot);
appender.put(parentName, nodeRoot);
}
}
private List<FileHeader> sortedHeaders(List<FileHeader> headers) {
List<FileHeader> sortedHeaders = new ArrayList<>();
Map<Integer, FileHeader> mapHeaders = new TreeMap<>();
headers.forEach(header -> mapHeaders.put(new Integer(0).equals(header.getFileNameW().length()) ? header.getFileNameString().length() : header.getFileNameW().length(), header));
for (Map.Entry<Integer, FileHeader> entry : mapHeaders.entrySet()){
for (FileHeader header : headers) {
if (entry.getKey().equals(new Integer(0).equals(header.getFileNameW().length()) ? header.getFileNameString().length() : header.getFileNameW().length())) {
sortedHeaders.add(header);
}
}
}
return sortedHeaders;
}
private static String getLast2FileName(String fullName, String seperator, String rootName) {
if (fullName.endsWith(seperator)) {
fullName = fullName.substring(0, fullName.length()-1);
}
// 1.获取剩余部分
int endIndex = fullName.lastIndexOf(seperator);
String leftPath = fullName.substring(0, endIndex == -1 ? 0 : endIndex);
if (leftPath.length() > 1) {
// 2.获取倒数第二个
return getLastFileName(leftPath, seperator);
} else {
return rootName;
}
}
private static String getLastFileName(String fullName, String seperator) {
if (fullName.endsWith(seperator)) {
fullName = fullName.substring(0, fullName.length()-1);
}
String newName = fullName;
if (fullName.contains(seperator)) {
newName = fullName.substring(fullName.lastIndexOf(seperator) + 1);
}
return newName;
}
public static Comparator<FileNode> sortComparator = new Comparator<FileNode>() {
final Collator cmp = Collator.getInstance(Locale.US);
@Override
public int compare(FileNode o1, FileNode o2) {
// 判断两个对比对象是否是开头包含数字,如果包含数字则获取数字并按数字真正大小进行排序
BigDecimal num1,num2;
if (null != (num1 = isStartNumber(o1))
&& null != (num2 = isStartNumber(o2))) {
return num1.subtract(num2).intValue();
}
CollationKey c1 = cmp.getCollationKey(o1.getOriginName());
CollationKey c2 = cmp.getCollationKey(o2.getOriginName());
return cmp.compare(c1.getSourceString(), c2.getSourceString());
}
};
private static BigDecimal isStartNumber(FileNode src) {
Matcher matcher = pattern.matcher(src.getOriginName());
if (matcher.find()) {
return new BigDecimal(matcher.group());
}
return null;
}
public static class FileNode {
private String originName;
private String fileName;
private String parentFileName;
private boolean directory;
//用于图片预览时寻址
private String fileKey;
private List<FileNode> childList;
public FileNode(String originName, String fileName, String parentFileName, List<FileNode> childList, boolean directory) {
this.originName = originName;
this.fileName = fileName;
this.parentFileName = parentFileName;
this.childList = childList;
this.directory = directory;
}
public FileNode(String originName, String fileName, String parentFileName, List<FileNode> childList, boolean directory,String fileKey) {
this.originName = originName;
this.fileName = fileName;
this.parentFileName = parentFileName;
this.childList = childList;
this.directory = directory;
this.fileKey=fileKey;
}
public String getFileKey() {
return fileKey;
}
public void setFileKey(String fileKey) {
this.fileKey = fileKey;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getParentFileName() {
return parentFileName;
}
public void setParentFileName(String parentFileName) {
this.parentFileName = parentFileName;
}
public List<FileNode> getChildList() {
return childList;
}
public void setChildList(List<FileNode> childList) {
this.childList = childList;
}
@Override
public String toString() {
try {
return new ObjectMapper().writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return "";
}
}
public String getOriginName() {
return originName;
}
public void setOriginName(String originName) {
this.originName = originName;
}
public boolean isDirectory() {
return directory;
}
public void setDirectory(boolean directory) {
this.directory = directory;
}
}
class ZipExtractorWorker implements Runnable {
private final List<Map<String, ZipArchiveEntry>> entriesToBeExtracted;
private final ZipFile zipFile;
private final String filePath;
public ZipExtractorWorker(List<Map<String, ZipArchiveEntry>> entriesToBeExtracted, ZipFile zipFile, String filePath) {
this.entriesToBeExtracted = entriesToBeExtracted;
this.zipFile = zipFile;
this.filePath = filePath;
}
@Override
public void run() {
for (Map<String, ZipArchiveEntry> entryMap : entriesToBeExtracted) {
String childName = entryMap.keySet().iterator().next();
ZipArchiveEntry entry = entryMap.values().iterator().next();
try {
extractZipFile(childName, zipFile.getInputStream(entry));
} catch (IOException e) {
e.printStackTrace();
}
}
try {
zipFile.close();
} catch (IOException e) {
e.printStackTrace();
}
if (new File(filePath).exists()) {
new File(filePath).delete();
}
}
private void extractZipFile(String childName, InputStream zipFile) {
String outPath = fileDir + childName;
try (OutputStream ot = new FileOutputStream(outPath)){
byte[] inByte = new byte[1024];
int len;
while ((-1 != (len = zipFile.read(inByte)))){
ot.write(inByte, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class SevenZExtractorWorker implements Runnable {
private final List<Map<String, SevenZArchiveEntry>> entriesToBeExtracted;
private final String filePath;
public SevenZExtractorWorker(List<Map<String, SevenZArchiveEntry>> entriesToBeExtracted, String filePath) {
this.entriesToBeExtracted = entriesToBeExtracted;
this.filePath = filePath;
}
@Override
public void run() {
try {
SevenZFile sevenZFile = new SevenZFile(new File(filePath));
SevenZArchiveEntry entry = sevenZFile.getNextEntry();
while (entry != null) {
if (entry.isDirectory()) {
entry = sevenZFile.getNextEntry();
continue;
}
String childName = "default_file";
SevenZArchiveEntry entry1;
for (Map<String, SevenZArchiveEntry> entryMap : entriesToBeExtracted) {
childName = entryMap.keySet().iterator().next();
entry1 = entryMap.values().iterator().next();
if (entry.getName().equals(entry1.getName())) {
break;
}
}
FileOutputStream out = new FileOutputStream(fileDir + childName);
byte[] content = new byte[(int) entry.getSize()];
sevenZFile.read(content, 0, content.length);
out.write(content);
out.close();
entry = sevenZFile.getNextEntry();
}
sevenZFile.close();
} catch (IOException e) {
e.printStackTrace();
}
if (new File(filePath).exists()) {
new File(filePath).delete();
}
}
}
class RarExtractorWorker implements Runnable {
private final List<Map<String, FileHeader>> headersToBeExtracted;
private final Archive archive;
/**
* 用以删除源文件
*/
private final String filePath;
public RarExtractorWorker(List<Map<String, FileHeader>> headersToBeExtracted, Archive archive, String filePath) {
this.headersToBeExtracted = headersToBeExtracted;
this.archive = archive;
this.filePath = filePath;
}
@Override
public void run() {
for (Map<String, FileHeader> entryMap : headersToBeExtracted) {
String childName = entryMap.keySet().iterator().next();
extractRarFile(childName, entryMap.values().iterator().next(), archive);
}
try {
archive.close();
} catch (IOException e) {
e.printStackTrace();
}
if (new File(filePath).exists()) {
new File(filePath).delete();
}
}
private void extractRarFile(String childName, FileHeader header, Archive archive) {
String outPath = fileDir + childName;
try(OutputStream ot = new FileOutputStream(outPath)) {
archive.extractFile(header, ot);
} catch (IOException | RarException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,99 @@
package cn.keking.web.controller;
import cn.keking.config.ConfigConstants;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import cn.keking.model.ReturnResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.*;
/**
*
* @author yudian-it
* @date 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;
@RequestMapping(value = "fileUpload", method = RequestMethod.POST)
public String fileUpload(@RequestParam("file") MultipartFile file) throws JsonProcessingException {
// 获取文件名
String fileName = file.getOriginalFilename();
//判断是否为IE浏览器的文件名IE浏览器下文件名会带有盘符信息
// 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);
}
// 判断是否存在同名文件
if (existsFile(fileName)) {
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "存在同名文件,请先删除原有文件再次上传", null));
}
File outFile = new File(fileDir + demoPath);
if (!outFile.exists()) {
outFile.mkdirs();
}
logger.info("上传文件:{}", fileDir + demoPath + fileName);
try(InputStream in = file.getInputStream(); OutputStream out = new FileOutputStream(fileDir + demoPath + fileName)) {
StreamUtils.copy(in, out);
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null));
} catch (IOException e) {
logger.error("文件上传失败", e);
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(1, "FAILURE", null));
}
}
@RequestMapping(value = "deleteFile", method = RequestMethod.GET)
public String deleteFile(String fileName) throws JsonProcessingException {
if (fileName.contains("/")) {
fileName = fileName.substring(fileName.lastIndexOf("/") + 1);
}
File file = new File(fileDir + demoPath + fileName);
logger.info("删除文件:{}", file.getAbsolutePath());
if (file.exists()) {
file.delete();
}
return new ObjectMapper().writeValueAsString(new ReturnResponse<String>(0, "SUCCESS", null));
}
@RequestMapping(value = "listFiles", method = RequestMethod.GET)
public String getFiles() throws JsonProcessingException {
List<Map<String, String>> list = new ArrayList<>();
File file = new File(fileDir + demoPath);
if (file.exists()) {
Arrays.stream(Objects.requireNonNull(file.listFiles())).forEach(file1 -> {
Map<String, String> fileName = new HashMap();
fileName.put("fileName", demoDir + "/" + file1.getName());
list.add(fileName);
});
}
return new ObjectMapper().writeValueAsString(list);
}
private boolean existsFile(String fileName) {
File file = new File(fileDir + demoPath + fileName);
return file.exists();
}
}

View File

@@ -0,0 +1,24 @@
package cn.keking.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* 页面跳转
* @author yudian-it
* @date 2017/12/27
*/
@Controller
public class IndexController {
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String go2Index(){
return "index";
}
@RequestMapping(value = "/", method = RequestMethod.GET)
public String root() {
return "redirect:/index";
}
}

View File

@@ -0,0 +1,100 @@
package cn.keking.web.controller;
import cn.keking.model.FileAttribute;
import cn.keking.service.FilePreview;
import cn.keking.service.FilePreviewFactory;
import cn.keking.service.cache.CacheService;
import cn.keking.utils.DownloadUtils;
import cn.keking.service.FilePreviewCommonService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.List;
/**
* @author yudian-it
*/
@Controller
public class OnlinePreviewController {
private final Logger logger = LoggerFactory.getLogger(OnlinePreviewController.class);
private final FilePreviewFactory previewFactory;
private final CacheService cacheService;
private final FilePreviewCommonService filePreviewCommonService;
private final DownloadUtils downloadUtils;
public OnlinePreviewController(FilePreviewFactory filePreviewFactory, FilePreviewCommonService filePreviewCommonService, CacheService cacheService, DownloadUtils downloadUtils) {
this.previewFactory = filePreviewFactory;
this.filePreviewCommonService = filePreviewCommonService;
this.cacheService = cacheService;
this.downloadUtils = downloadUtils;
}
@RequestMapping(value = "/onlinePreview")
public String onlinePreview(String url, Model model, HttpServletRequest req) {
FileAttribute fileAttribute = filePreviewCommonService.getFileAttribute(url,req);
FilePreview filePreview = previewFactory.get(fileAttribute);
logger.info("预览文件url{}previewType{}", url, fileAttribute.getType());
return filePreview.filePreviewHandle(url, model, fileAttribute);
}
@RequestMapping(value = "/picturesPreview")
public String picturesPreview(Model model, HttpServletRequest req) throws UnsupportedEncodingException {
String urls = req.getParameter("urls");
String currentUrl = req.getParameter("currentUrl");
logger.info("预览文件url{}urls{}", currentUrl, urls);
// 路径转码
String decodedUrl = URLDecoder.decode(urls, "utf-8");
String decodedCurrentUrl = URLDecoder.decode(currentUrl, "utf-8");
// 抽取文件并返回文件列表
String[] imgs = decodedUrl.split("\\|");
List<String> imgUrls = Arrays.asList(imgs);
model.addAttribute("imgUrls", imgUrls);
model.addAttribute("currentUrl",decodedCurrentUrl);
return "picture";
}
/**
* 根据url获取文件内容
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取
*
* @param urlPath url
* @param response response
*/
@RequestMapping(value = "/getCorsFile", method = RequestMethod.GET)
public void getCorsFile(String urlPath, HttpServletResponse response) {
logger.info("下载跨域pdf文件url{}", urlPath);
try {
byte[] bytes = downloadUtils.getBytesFromUrl(urlPath);
downloadUtils.saveBytesToOutStream(bytes, response.getOutputStream());
} catch (IOException e) {
logger.error("下载跨域pdf文件异常url{}", urlPath, e);
}
}
/**
* 通过api接口入队
* @param url 请编码后在入队
*/
@RequestMapping("/addTask")
@ResponseBody
public String addQueueTask(String url) {
logger.info("添加转码队列url{}", url);
cacheService.addQueueTask(url);
return "success";
}
}

View File

@@ -0,0 +1,62 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import cn.keking.config.WatermarkConfigConstants;
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("pdfDownloadDisable", ConfigConstants.getPdfDownloadDisable());
request.setAttribute("fileKey", httpRequest.getParameter("fileKey"));
request.setAttribute("switchDisabled", ConfigConstants.getOfficePreviewSwitchDisabled());
}
/**
* 设置水印属性
* @param request request
*/
private void setWatermarkAttribute(ServletRequest request) {
String watermarkTxt = request.getParameter("watermarkTxt");
request.setAttribute("watermarkTxt", watermarkTxt != null ? watermarkTxt : WatermarkConfigConstants.getWatermarkTxt());
request.setAttribute("watermarkXSpace", WatermarkConfigConstants.getWatermarkXSpace());
request.setAttribute("watermarkYSpace", WatermarkConfigConstants.getWatermarkYSpace());
request.setAttribute("watermarkFont", WatermarkConfigConstants.getWatermarkFont());
request.setAttribute("watermarkFontsize", WatermarkConfigConstants.getWatermarkFontsize());
request.setAttribute("watermarkColor", WatermarkConfigConstants.getWatermarkColor());
request.setAttribute("watermarkAlpha", WatermarkConfigConstants.getWatermarkAlpha());
request.setAttribute("watermarkWidth", WatermarkConfigConstants.getWatermarkWidth());
request.setAttribute("watermarkHeight", WatermarkConfigConstants.getWatermarkHeight());
request.setAttribute("watermarkAngle", WatermarkConfigConstants.getWatermarkAngle());
}
@Override
public void destroy() {
}
}

View File

@@ -0,0 +1,58 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
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;
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":")
.append(request.getServerPort()).append(((HttpServletRequest) request).getContextPath()).append("/");
String baseUrlTmp = ConfigConstants.getBaseUrl();
if (baseUrlTmp != null && !ConfigConstants.DEFAULT_BASE_URL.equals(baseUrlTmp.toLowerCase())) {
if (!baseUrlTmp.endsWith("/")) {
baseUrlTmp = baseUrlTmp.concat("/");
}
baseUrl = baseUrlTmp;
} else {
baseUrl = pathBuilder.toString();
}
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,66 @@
package cn.keking.web.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author yudian-it
* @date 2017/11/30
*/
@Configuration
public class FilterConfiguration {
@Bean
public FilterRegistrationBean getChinesePathFilter() {
ChinesePathFilter filter = new ChinesePathFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
return registrationBean;
}
@Bean
public FilterRegistrationBean getTrustHostFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
filterUri.add("/getCorsFile");
filterUri.add("/addTask");
TrustHostFilter filter = new TrustHostFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean getBaseUrlFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/index");
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
BaseUrlFilter filter = new BaseUrlFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
@Bean
public FilterRegistrationBean getWatermarkConfigFilter() {
Set<String> filterUri = new HashSet<>();
filterUri.add("/onlinePreview");
filterUri.add("/picturesPreview");
AttributeSetFilter filter = new AttributeSetFilter();
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(filter);
registrationBean.setUrlPatterns(filterUri);
return registrationBean;
}
}

View File

@@ -0,0 +1,75 @@
package cn.keking.web.filter;
import cn.keking.config.ConfigConstants;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.FileCopyUtils;
import javax.servlet.*;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
/**
* @author chenjh
* @since 2020/2/18 19:13
*/
public class TrustHostFilter implements Filter {
private String notTrustHost;
@Override
public void init(FilterConfig filterConfig) {
ClassPathResource classPathResource = new ClassPathResource("web/notTrustHost.html");
try {
classPathResource.getInputStream();
byte[] bytes = FileCopyUtils.copyToByteArray(classPathResource.getInputStream());
this.notTrustHost = 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 = getSourceUrl(request);
String host = getHost(url);
if (host != null &&!ConfigConstants.getTrustHostSet().isEmpty() && !ConfigConstants.getTrustHostSet().contains(host)) {
String html = this.notTrustHost.replace("${current_host}", host);
response.getWriter().write(html);
response.getWriter().close();
}
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
private String getSourceUrl(ServletRequest request) {
String url = request.getParameter("url");
String currentUrl = request.getParameter("currentUrl");
String urlPath = request.getParameter("urlPath");
if (StringUtils.isNotBlank(url)) {
return url;
}
if (StringUtils.isNotBlank(currentUrl)) {
return currentUrl;
}
if (StringUtils.isNotBlank(urlPath)) {
return urlPath;
}
return null;
}
private String getHost(String urlStr) {
try {
URL url = new URL(urlStr);
return url.getHost().toLowerCase();
} catch (MalformedURLException ignored) {
}
return null;
}
}