From dcf37b94db9f2f7c943ffa38b62273d15d0261ae Mon Sep 17 00:00:00 2001 From: kl <632104866@QQ.com> Date: Wed, 17 Jan 2018 14:10:40 +0800 Subject: [PATCH] =?UTF-8?q?1.=E4=BF=AE=E6=94=B9=E5=8C=85=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=B8=BAcn.keking,=202.=E6=96=B0=E5=A2=9E=E5=8E=8B=E7=BC=A9?= =?UTF-8?q?=E5=8C=85=E5=86=85=E6=96=87=E4=BB=B6=E5=90=8D=E7=A7=B0=E6=8E=92?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jodconverter-core/pom.xml | 2 +- jodconverter-web/pom.xml | 2 +- .../cn/keking/FilePreviewApplication.java | 18 + .../java/cn/keking/config/RedissonConfig.java | 242 +++++++++ .../extend/ControlDocumentFormatRegistry.java | 211 ++++++++ .../cn/keking/filters/ChinesePathFilter.java | 33 ++ .../keking/filters/FilterConfiguration.java | 23 + .../java/cn/keking/param/ReturnResponse.java | 57 +++ .../java/cn/keking/utils/ConverterUtils.java | 68 +++ .../java/cn/keking/utils/DeleteFileUtil.java | 74 +++ .../java/cn/keking/utils/DownloadUtils.java | 198 ++++++++ .../cn/keking/utils/FileCharsetDetector.java | 157 ++++++ .../main/java/cn/keking/utils/FileUtils.java | 238 +++++++++ .../java/cn/keking/utils/OfficeToPdf.java | 112 +++++ .../java/cn/keking/utils/ShedulerClean.java | 16 + .../java/cn/keking/utils/SimTextUtil.java | 24 + .../main/java/cn/keking/utils/ZipReader.java | 459 ++++++++++++++++++ .../keking/web/controller/FileController.java | 113 +++++ .../web/controller/IndexController.java | 24 + .../controller/OnlinePreviewController.java | 209 ++++++++ .../FilePreviewApplicationTests.java | 2 +- pom.xml | 2 +- 22 files changed, 2280 insertions(+), 4 deletions(-) create mode 100644 jodconverter-web/src/main/java/cn/keking/FilePreviewApplication.java create mode 100644 jodconverter-web/src/main/java/cn/keking/config/RedissonConfig.java create mode 100644 jodconverter-web/src/main/java/cn/keking/extend/ControlDocumentFormatRegistry.java create mode 100644 jodconverter-web/src/main/java/cn/keking/filters/ChinesePathFilter.java create mode 100644 jodconverter-web/src/main/java/cn/keking/filters/FilterConfiguration.java create mode 100644 jodconverter-web/src/main/java/cn/keking/param/ReturnResponse.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/ConverterUtils.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/DeleteFileUtil.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/DownloadUtils.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/FileCharsetDetector.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/OfficeToPdf.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/ShedulerClean.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/SimTextUtil.java create mode 100644 jodconverter-web/src/main/java/cn/keking/utils/ZipReader.java create mode 100644 jodconverter-web/src/main/java/cn/keking/web/controller/FileController.java create mode 100644 jodconverter-web/src/main/java/cn/keking/web/controller/IndexController.java create mode 100644 jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java diff --git a/jodconverter-core/pom.xml b/jodconverter-core/pom.xml index 258a5031..e1f29d72 100644 --- a/jodconverter-core/pom.xml +++ b/jodconverter-core/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 - com.yudianbank + cn.keking jodconverter-core 1.0-SNAPSHOT jar diff --git a/jodconverter-web/pom.xml b/jodconverter-web/pom.xml index 1f7d1f93..9d0885c9 100644 --- a/jodconverter-web/pom.xml +++ b/jodconverter-web/pom.xml @@ -48,7 +48,7 @@ test - com.yudianbank + cn.keking jodconverter-core 1.0-SNAPSHOT diff --git a/jodconverter-web/src/main/java/cn/keking/FilePreviewApplication.java b/jodconverter-web/src/main/java/cn/keking/FilePreviewApplication.java new file mode 100644 index 00000000..c1114df6 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/FilePreviewApplication.java @@ -0,0 +1,18 @@ +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; +import java.util.Properties; + +@SpringBootApplication +@EnableScheduling +@ComponentScan(value = "cn.keking.*") +public class FilePreviewApplication { + public static void main(String[] args) { + Properties properties = System.getProperties(); + System.out.println(properties.get("user.dir")); + SpringApplication.run(FilePreviewApplication.class, args); + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/config/RedissonConfig.java b/jodconverter-web/src/main/java/cn/keking/config/RedissonConfig.java new file mode 100644 index 00000000..78aa7861 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/config/RedissonConfig.java @@ -0,0 +1,242 @@ +package cn.keking.config; + +import io.netty.channel.nio.NioEventLoopGroup; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.client.codec.Codec; +import org.redisson.config.Config; +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 客户端配置 + */ +@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(destroyMethod = "shutdown") + RedissonClient redisson() 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 Redisson.create(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; + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/extend/ControlDocumentFormatRegistry.java b/jodconverter-web/src/main/java/cn/keking/extend/ControlDocumentFormatRegistry.java new file mode 100644 index 00000000..bc82ec07 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/extend/ControlDocumentFormatRegistry.java @@ -0,0 +1,211 @@ +package cn.keking.extend; + +import com.google.common.collect.Maps; +import com.sun.star.beans.PropertyValue; +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 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 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 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 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); + } + + /** + * 创建默认的导出属性 + * @return + */ + private PropertyValue[] getCommonPropertyValue() { + PropertyValue[] aFilterData = new PropertyValue[11]; + // 不显示文档标题 + aFilterData[0] = new PropertyValue(); + aFilterData[0].Name = "DisplayPDFDocumentTitle"; + aFilterData[0].Value= true; + // 导出文件编码方式 + aFilterData[1] = new PropertyValue(); + aFilterData[1].Name = "Encoding"; + aFilterData[1].Value= "UTF-8"; + // 隐藏工具条 + aFilterData[2] = new PropertyValue(); + aFilterData[2].Name = "HideViewerToolbar"; + aFilterData[2].Value= false; + // 隐藏窗口控制条 + aFilterData[3] = new PropertyValue(); + aFilterData[3].Name = "HideViewerWindowControls"; + aFilterData[3].Value= true; + // 全屏展示 + aFilterData[4] = new PropertyValue(); + aFilterData[4].Name = "OpenInFullScreenMode"; + aFilterData[4].Value= false; + // 第一页左边展示 + aFilterData[5] = new PropertyValue(); + aFilterData[5].Name = "MathToMathType"; + aFilterData[5].Value= true; + // 文档标题内容 + aFilterData[6] = new PropertyValue(); + aFilterData[6].Name = "Watermark"; + aFilterData[6].Value= "KEKING.CN"; + // 导出文件编码方式 + aFilterData[7] = new PropertyValue(); + aFilterData[7].Name = "CharacterSet"; + aFilterData[7].Value= "UTF-8"; + // 导出文件编码方式 + aFilterData[8] = new PropertyValue(); + aFilterData[8].Name = "Encoding"; + aFilterData[8].Value= "UTF-8"; + // 导出文件编码方式 + aFilterData[9] = new PropertyValue(); + aFilterData[9].Name = "CharSet"; + aFilterData[9].Value= "UTF-8"; + // 导出文件编码方式 + aFilterData[10] = new PropertyValue(); + aFilterData[10].Name = "charset"; + aFilterData[10].Value= "UTF-8"; + return aFilterData; + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/filters/ChinesePathFilter.java b/jodconverter-web/src/main/java/cn/keking/filters/ChinesePathFilter.java new file mode 100644 index 00000000..cb241a36 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/filters/ChinesePathFilter.java @@ -0,0 +1,33 @@ +package cn.keking.filters; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +/** + * + * @author yudian-it + * @date 2017/11/30 + */ +public class ChinesePathFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + request.setCharacterEncoding("UTF-8"); + response.setCharacterEncoding("UTF-8"); + StringBuilder pathBuilder = new StringBuilder(); + pathBuilder.append(request.getScheme()).append("://").append(request.getServerName()).append(":") + .append(request.getServerPort()).append(((HttpServletRequest)request).getContextPath()).append("/"); + request.setAttribute("baseUrl", pathBuilder.toString()); + chain.doFilter(request, response); + } + + @Override + public void destroy() { + + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/filters/FilterConfiguration.java b/jodconverter-web/src/main/java/cn/keking/filters/FilterConfiguration.java new file mode 100644 index 00000000..a57c15f5 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/filters/FilterConfiguration.java @@ -0,0 +1,23 @@ +package cn.keking.filters; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * + * @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; + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/param/ReturnResponse.java b/jodconverter-web/src/main/java/cn/keking/param/ReturnResponse.java new file mode 100644 index 00000000..b97539c9 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/param/ReturnResponse.java @@ -0,0 +1,57 @@ +package cn.keking.param; + +import java.io.Serializable; + +/** + * 接口返回值结构 + * @author yudian-it + * @date 2017/11/17 + */ +public class ReturnResponse 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; + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/ConverterUtils.java b/jodconverter-web/src/main/java/cn/keking/utils/ConverterUtils.java new file mode 100644 index 00000000..6232231a --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/ConverterUtils.java @@ -0,0 +1,68 @@ +package cn.keking.utils; + +import com.sun.star.document.UpdateDocMode; +import cn.keking.extend.ControlDocumentFormatRegistry; +import org.artofsolving.jodconverter.OfficeDocumentConverter; +import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration; +import org.artofsolving.jodconverter.office.OfficeManager; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +/** + * 创建文件转换器 + * + * @author yudian-it + * @date 2017/11/13 + */ +@Component +public class ConverterUtils { + + @Value("${office.home}") + String officeHome; +// OpenOfficeConnection connection; + OfficeManager officeManager; + + @PostConstruct + public void initOfficeManager() { + //// connection = new SocketOpenOfficeConnection(host,8100); +//// connection.connect(); + DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration(); + configuration.setOfficeHome(officeHome); + configuration.setPortNumber(8100); + officeManager = configuration.buildOfficeManager(); + officeManager.start(); + // 设置任务执行超时为5分钟 + // configuration.setTaskExecutionTimeout(1000 * 60 * 5L);// + // 设置任务队列超时为24小时 + // configuration.setTaskQueueTimeout(1000 * 60 * 60 * 24L);// + } + + public OfficeDocumentConverter getDocumentConverter() { + OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, new ControlDocumentFormatRegistry()); + converter.setDefaultLoadProperties(getLoadProperties()); + return converter; + } + + private Map getLoadProperties() { + Map loadProperties = new HashMap<>(10); + loadProperties.put("Hidden", true); + loadProperties.put("ReadOnly", true); + loadProperties.put("UpdateDocMode", UpdateDocMode.QUIET_UPDATE); + loadProperties.put("CharacterSet", Charset.forName("UTF-8").name()); + return loadProperties; + } + + @PreDestroy + public void destroyOfficeManager(){ + if (null != officeManager && officeManager.isRunning()) { + officeManager.stop(); + } + } + +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/DeleteFileUtil.java b/jodconverter-web/src/main/java/cn/keking/utils/DeleteFileUtil.java new file mode 100644 index 00000000..fc54708b --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/DeleteFileUtil.java @@ -0,0 +1,74 @@ +package cn.keking.utils; + +import java.io.File; + +public class DeleteFileUtil { + /** + * 删除单个文件 + * + * @param fileName + * 要删除的文件的文件名 + * @return 单个文件删除成功返回true,否则返回false + */ + public static boolean deleteFile(String fileName) { + File file = new File(fileName); + // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除 + if (file.exists() && file.isFile()) { + if (file.delete()) { + System.out.println("删除单个文件" + fileName + "成功!"); + return true; + } else { + System.out.println("删除单个文件" + fileName + "失败!"); + return false; + } + } else { + System.out.println("删除单个文件失败:" + 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())) { + System.out.println("删除目录失败:" + dir + "不存在!"); + return false; + } + boolean flag = true; + // 删除文件夹中的所有文件包括子目录 + File[] files = dirFile.listFiles(); + for (int i = 0; i < 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; + } + } + if (!flag) { + System.out.println("删除目录失败!"); + return false; + } + return true; + } + +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/DownloadUtils.java b/jodconverter-web/src/main/java/cn/keking/utils/DownloadUtils.java new file mode 100644 index 00000000..b7bb90b2 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/DownloadUtils.java @@ -0,0 +1,198 @@ +package cn.keking.utils; + +import cn.keking.param.ReturnResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import java.io.*; +import java.net.*; +import java.nio.charset.Charset; +import java.util.UUID; + +/** + * @author yudian-it + */ +@Component +public class DownloadUtils { + + @Value("${file.dir}") + String fileDir; + + /** + * 一开始测试的时候发现有些文件没有下载下来,而有些可以;当时也是郁闷了好一阵,但是最终还是不得解 + * 再次测试的时候,通过前台对比url发现,原来参数中有+号特殊字符存在,但是到后之后却变成了空格,突然恍然大悟 + * 应该是转义出了问题,url转义中会把+号当成空格来计算,所以才会出现这种情况,遂想要通过整体替换空格为加号,因为url + * 中的参数部分是不会出现空格的,但是文件名中就不好确定了,所以只对url参数部分做替换 + * 注: 针对URLEncoder.encode(s,charset)会将空格转成+的情况需要做下面的替换工作 + * @param urlAddress + * @param type + * @return + */ + public ReturnResponse downLoad(String urlAddress, String type, String fileName){ + ReturnResponse response = new ReturnResponse<>(0, "下载成功!!!", ""); + URL url = null; + try { + urlAddress = replacePlusMark(urlAddress); + urlAddress = encodeUrlParam(urlAddress); + // 因为tomcat不能处理'+'号,所以讲'+'号替换成'%20%' + urlAddress = urlAddress.replaceAll("\\+", "%20"); + url = new URL(urlAddress); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + 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 { + URLConnection connection = url.openConnection(); + InputStream in = connection.getInputStream(); + + FileOutputStream os = new FileOutputStream(realPath); + byte[] buffer = new byte[4 * 1024]; + int read; + while ((read = in.read(buffer)) > 0) { + os.write(buffer, 0, read); + } + os.close(); + in.close(); + response.setContent(realPath); + // 同样针对类txt文件,如果成功msg包含的是转换后的文件名 + response.setMsg(fileName); + + // txt转换文件编码为utf8 + if("txt".equals(type)){ + convertTextPlainFileCharsetToUtf8(realPath); + } + + return response; + } catch (IOException e) { + e.printStackTrace(); + response.setCode(1); + response.setContent(null); + if (e instanceof FileNotFoundException) { + response.setMsg("文件不存在!!!"); + }else { + response.setMsg(e.getMessage()); + } + return response; + } + } + + /** + * 注:可能是原来因为前端通过encodeURI来编码的,因为通过encodeURI编码+会被转成+号(亦即没有转), + * 而通过encodeURIComponent则会转成%2B,这样URLDecoder是可以正确处理的,所以也就没有必要在这里替换了 + * 转换url参数部分的空格为加号(因为在url编解码的过程中出现+转为空格的情况) + * @param urlAddress + * @return + */ + private String replacePlusMark(String urlAddress) { + if (urlAddress.contains("?")) { + String nonParamStr = urlAddress.substring(0,urlAddress.indexOf("?") + 1); + String paramStr = urlAddress.substring(nonParamStr.length()); + return nonParamStr + paramStr.replace(" ", "+"); + } + return urlAddress; + } + + /** + * 对最有一个路径进行转码 + * @param urlAddress + * http://192.168.2.111:8013/demo/Handle中文.zip + * @return + */ + private String encodeUrlParam(String urlAddress) { + String newUrl = ""; + try { + String path = ""; + String param = ""; + if (urlAddress.contains("?")) { + path = urlAddress.substring(0, urlAddress.indexOf("?")); + param = urlAddress.substring(urlAddress.indexOf("?")); + }else { + path = urlAddress; + } + String lastPath = path.substring(path.lastIndexOf("/") + 1); + String leftPath = path.substring(0, path.lastIndexOf("/") + 1); + String encodeLastPath = URLEncoder.encode(lastPath, "UTF-8"); + newUrl += leftPath + encodeLastPath; + if (urlAddress.contains("?")) { + newUrl += param; + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return newUrl; + } + + + + /** + * 因为jodConvert2.1不支持ms2013版本的office转换,这里偷懒,尝试看改一下文件类型,让jodConvert2.1去 + * 处理ms2013,看结果如何,如果问题很大的话只能采取其他方式,如果没有问题,暂时使用该版本来转换 + * @param type + * @return + */ + private String dealWithMS2013(String type) { + String newType = null; + switch (type){ + case "docx": + newType = "doc"; + break; + case "xlsx": + newType = "doc"; + break; + case "pptx": + newType = "ppt"; + break; + default: + newType = type; + break; + } + return newType; + } + + /** + * 转换文本文件编码为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),"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); + } + } + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/FileCharsetDetector.java b/jodconverter-web/src/main/java/cn/keking/utils/FileCharsetDetector.java new file mode 100644 index 00000000..63bfbc6b --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/FileCharsetDetector.java @@ -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()); + } + + /** + *
+   * 获取文件的编码
+   * @param file
+   *            File对象实例
+   * @param languageHint
+   *            语言提示区域代码 @see #nsPSMDetector ,取值如下:
+   *             1 : Japanese
+   *             2 : Chinese
+   *             3 : Simplified Chinese
+   *             4 : Traditional Chinese
+   *             5 : Korean
+   *             6 : Dont know(default)
+   * 
+ * + * @return 文件编码,eg:UTF-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 + "]"; + } + } + +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java b/jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java new file mode 100644 index 00000000..f7ad589b --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/FileUtils.java @@ -0,0 +1,238 @@ +package cn.keking.utils; + +import com.google.common.collect.Lists; +import org.redisson.api.RMapCache; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.*; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * + * @author yudian-it + * @date 2017/11/13 + */ +@Component +public class FileUtils { + + final String REDIS_FILE_PREVIEW_PDF_KEY = "converted-preview-pdf-file"; + final String REDIS_FILE_PREVIEW_IMGS_KEY = "converted-preview-imgs-file";//压缩包内图片文件集合 + @Autowired + RedissonClient redissonClient; + @Value("${file.dir}") + String fileDir; + + @Value("${converted.file.charset}") + String charset; + + @Value("${simText}") + String[] simText; + /** + * 已转换过的文件集合(redis缓存) + * @return + */ + public Map listConvertedFiles() { + RMapCache convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_KEY); + return convertedList; + } + + /** + * 已转换过的文件,根据文件名获取 + * @return + */ + public String getConvertedFile(String key) { + RMapCache convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_KEY); + return convertedList.get(key); + } + + /** + * 查看文件类型(防止参数中存在.点号或者其他特殊字符,所以先抽取文件名,然后再获取文件类型) + * + * @param url + * @return + */ + public String typeFromUrl(String url) { + String nonPramStr = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?") : url.length()); + String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1); + String fileType = fileName.substring(fileName.lastIndexOf(".") + 1); + if (listPictureTypes().contains(fileType.toLowerCase())) { + fileType = "picture"; + } + if (listArchiveTypes().contains(fileType.toLowerCase())) { + fileType = "compress"; + } + if (listOfficeTypes().contains(fileType.toLowerCase())) { + fileType = "office"; + } + if (Arrays.asList(simText).contains(fileType.toLowerCase())) { + fileType = "simText"; + } + return fileType; + } + /** + * 从url中剥离出文件名 + * @param url + * 格式如:http://keking.ufile.ucloud.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.indexOf("?") != -1 ? url.indexOf("?"): url.length()); + String fileName = noQueryUrl.substring(noQueryUrl.lastIndexOf("/") + 1); + return fileName; + } + + /** + * 获取文件后缀 + * @param fileName + * @return + */ + public String getSuffixFromFileName(String fileName) { + String suffix = fileName.substring(fileName.lastIndexOf(".")); + return suffix; + } + + /** + * 从路径中获取 + * @param path + * 类似这种:C:\Users\yudian-it\Downloads + * @return + */ + public String getFileNameFromPath(String path) { + return path.substring(path.lastIndexOf(File.separator) + 1); + } + + public List listPictureTypes(){ + List list = Lists.newArrayList(); + list.add("jpg"); + list.add("jpeg"); + list.add("png"); + list.add("gif"); + list.add("bmp"); + return list; + } + + public List listArchiveTypes(){ + List list = Lists.newArrayList(); + list.add("rar"); + list.add("zip"); + list.add("jar"); + list.add("7-zip"); + list.add("tar"); + list.add("gzip"); + list.add("7z"); + return list; + } + + public List listOfficeTypes() { + List list = Lists.newArrayList(); + list.add("docx"); + list.add("doc"); + list.add("xls"); + list.add("xlsx"); + list.add("ppt"); + list.add("pptx"); + return list; + } + + /** + * 获取相对路径 + * @param absolutePath + * @return + */ + public String getRelativePath(String absolutePath) { + return absolutePath.substring(fileDir.length()); + } + + public void addConvertedFile(String fileName, String value){ + RMapCache convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_PDF_KEY); + convertedList.fastPut(fileName, value); + } + + /** + * 获取redis中压缩包内图片文件 + * @param fileKey + * @return + */ + public List getRedisImgUrls(String fileKey){ + RMapCache convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_IMGS_KEY); + return convertedList.get(fileKey); + } + + /** + * 设置redis中压缩包内图片文件 + * @param fileKey + * @param imgs + */ + public void setRedisImgUrls(String fileKey,List imgs){ + RMapCache convertedList = redissonClient.getMapCache(REDIS_FILE_PREVIEW_IMGS_KEY); + convertedList.fastPut(fileKey,imgs); + } + /** + * 判断文件编码格式 + * @param path + * @return + */ + public String getFileEncodeUTFGBK(String path){ + String enc = Charset.forName("GBK").name(); + File file = new File(path); + InputStream in= null; + 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 = Charset.forName("UTF-8").name(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + System.out.println("文件编码格式为:" + enc); + return enc; + } + + /** + * 对转换后的文件进行操作(改变编码方式) + * @param outFilePath + */ + public void doActionConvertedFile(String outFilePath) { + StringBuffer sb = new StringBuffer(); + try (InputStream inputStream = new FileInputStream(outFilePath); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charset))){ + String line; + while(null != (line = reader.readLine())){ + if (line.contains("charset=gb2312")) { + line = line.replace("charset=gb2312", "charset=utf-8"); + } + sb.append(line); + } + // 添加sheet控制头 + sb.append(""); + sb.append(""); + sb.append(""); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + // 重新写入文件 + try(FileOutputStream fos = new FileOutputStream(outFilePath); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos))){ + writer.write(sb.toString()); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/OfficeToPdf.java b/jodconverter-web/src/main/java/cn/keking/utils/OfficeToPdf.java new file mode 100644 index 00000000..2e50026f --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/OfficeToPdf.java @@ -0,0 +1,112 @@ +package cn.keking.utils; +import org.artofsolving.jodconverter.OfficeDocumentConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import java.io.File; + +/** + * @author yudian-it + */ +@Component +public class OfficeToPdf { + /** + * 获取OpenOffice.org 3的安装目录 + * + * @return OpenOffice.org 3的安装目录 + */ + @Autowired + ConverterUtils converterUtils; + /** + * 使Office2003-2007全部格式的文档(.doc|.docx|.xls|.xlsx|.ppt|.pptx) 转化为pdf文件
+ * + * @param inputFilePath + * 源文件路径,如:"e:/test.docx" + * @param outputFilePath + * 目标文件路径,如:"e:/test_docx.pdf" + * @return + */ + public boolean openOfficeToPDF(String inputFilePath, String outputFilePath) { + return office2pdf(inputFilePath, outputFilePath); + } + + + /** + * 转换文件 + * + * @param inputFile + * @param outputFilePath_end + * @param inputFilePath + * @param outputFilePath + * @param converter + */ + public static void converterFile(File inputFile, String outputFilePath_end, + String inputFilePath, String outputFilePath, + OfficeDocumentConverter converter) { + File outputFile = new File(outputFilePath_end); + // 假如目标路径不存在,则新建该路径 + if (!outputFile.getParentFile().exists()) { + outputFile.getParentFile().mkdirs(); + } + converter.convert(inputFile, outputFile); + } + + /** + * 使Office2003-2007全部格式的文档(.doc|.docx|.xls|.xlsx|.ppt|.pptx) 转化为pdf文件 + * + * @param inputFilePath + * 源文件路径,如:"e:/test.docx" + * @param outputFilePath + * 目标文件路径,如:"e:/test_docx.pdf" + * @return + */ + public boolean office2pdf(String inputFilePath, String outputFilePath) { + boolean flag = false; + OfficeDocumentConverter converter = converterUtils.getDocumentConverter(); + if (null != inputFilePath) { + File inputFile = new File(inputFilePath); + // 判断目标文件路径是否为空 + if (null == outputFilePath) { + // 转换后的文件路径 + String outputFilePath_end = getOutputFilePath(inputFilePath); + if (inputFile.exists()) {// 找不到源文件, 则返回 + converterFile(inputFile, outputFilePath_end, inputFilePath, + outputFilePath, converter); + flag = true; + } + } else { + if (inputFile.exists()) {// 找不到源文件, 则返回 + converterFile(inputFile, outputFilePath, inputFilePath, + outputFilePath, converter); + flag = true; + } + } +// officeManager.stop(); + } else { + flag = false; + } + return flag; + } + + /** + * 获取输出文件 + * + * @param inputFilePath + * @return + */ + public static String getOutputFilePath(String inputFilePath) { + String outputFilePath = inputFilePath.replaceAll("." + + getPostfix(inputFilePath), ".pdf"); + return outputFilePath; + } + + /** + * 获取inputFilePath的后缀名,如:"e:/test.pptx"的后缀名为:"pptx" + * + * @param inputFilePath + * @return + */ + public static String getPostfix(String inputFilePath) { + return inputFilePath.substring(inputFilePath.lastIndexOf(".") + 1); + } + +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/ShedulerClean.java b/jodconverter-web/src/main/java/cn/keking/utils/ShedulerClean.java new file mode 100644 index 00000000..d345cff1 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/ShedulerClean.java @@ -0,0 +1,16 @@ +package cn.keking.utils; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class ShedulerClean { + @Value("${file.dir}") + String fileDir; + +// @Scheduled(cron = "0 0 23 * * ?") //每晚23点执行一次 + public void clean(){ + System.out.println("执行一次清空文件夹"); + DeleteFileUtil.deleteDirectory(fileDir); + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/SimTextUtil.java b/jodconverter-web/src/main/java/cn/keking/utils/SimTextUtil.java new file mode 100644 index 00000000..1eac8707 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/SimTextUtil.java @@ -0,0 +1,24 @@ +package cn.keking.utils; + +import cn.keking.param.ReturnResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 读取类文本文件 + * @author yudian-it + * @date 2017/12/13 + */ +@Component +public class SimTextUtil { + @Value("${file.dir}") + String fileDir; + @Autowired + DownloadUtils downloadUtils; + + public ReturnResponse readSimText(String url, String fileName){ + ReturnResponse response = downloadUtils.downLoad(url, "txt", fileName); + return response; + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/utils/ZipReader.java b/jodconverter-web/src/main/java/cn/keking/utils/ZipReader.java new file mode 100644 index 00000000..1aba5201 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/utils/ZipReader.java @@ -0,0 +1,459 @@ +package cn.keking.utils; + +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 com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; + +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+"); + + @Autowired + FileUtils fileUtils; + @Value("${file.dir}") + String fileDir; + + ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + + /** + * 读取压缩文件 + * 文件压缩到统一目录fileDir下,并且命名使用压缩文件名+文件名因为文件名 + * 可能会重复(在系统中对于同一种类型的材料压缩文件内的文件是一样的,如果文件名 + * 重复,那么这里会被覆盖[同一个压缩文件中的不同目录中的相同文件名暂时不考虑]) + * 注: + *

+ * 文件名命名中的参数的说明: + * 1.archiveName,为避免解压的文件中有重名的文件会彼此覆盖,所以加上了archiveName,因为在ufile中archiveName + * 是不会重复的。 + * 2.level,这里层级结构的列表我是通过一个map来构造的,map的key是文件的名字,值是对应的文件,这样每次向map中 + * 加入节点的时候都会获取父节点是否存在,存在则会获取父节点的value并将当前节点加入到父节点的childList中(这里利用 + * 的是java语言的引用的特性)。 + *

+ * @param filePath + */ + public String readZipFile(String filePath,String fileKey) { + String archiveSeparator = "/"; + Map appender = Maps.newHashMap(); + List imgUrls=Lists.newArrayList(); + String baseUrl= (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl",0); + String archiveFileName = fileUtils.getFileNameFromPath(filePath); + try { + ZipFile zipFile = new ZipFile(filePath, fileUtils.getFileEncodeUTFGBK(filePath)); + Enumeration entries = zipFile.getEntries(); + // 排序 + entries = sortZipEntries(entries); + List> entriesToBeExtracted = Lists.newArrayList(); + 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; + String type=fileUtils.typeFromUrl(childName); + if (type.equalsIgnoreCase("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)); + fileUtils.setRedisImgUrls(fileKey,imgUrls); + return new ObjectMapper().writeValueAsString(appender.get("")); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + + + /** + * 排序zipEntries(对原来列表倒序) + * @param entries + */ + private Enumeration sortZipEntries(Enumeration entries) { + List sortedEntries = Lists.newArrayList(); + while(entries.hasMoreElements()){ + sortedEntries.add(entries.nextElement()); + } + Collections.sort(sortedEntries, Comparator.comparingInt(o -> o.getName().length())); + return Collections.enumeration(sortedEntries); + } + + public String unRar(String filePath,String fileKey){ + Map appender = Maps.newHashMap(); + List imgUrls=Lists.newArrayList(); + String baseUrl= (String) RequestContextHolder.currentRequestAttributes().getAttribute("baseUrl",0); + try { + Archive archive = new Archive(new File(filePath)); + List headers = archive.getFileHeaders(); + headers = sortedHeaders(headers); + String archiveFileName = fileUtils.getFileNameFromPath(filePath); + List> headersToBeExtracted = Lists.newArrayList(); + 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); + String type=fileUtils.typeFromUrl(childName); + if (type.equalsIgnoreCase("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)); + fileUtils.setRedisImgUrls(fileKey,imgUrls); + return new ObjectMapper().writeValueAsString(appender.get("")); + } catch (RarException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private void addNodes(Map appender, String parentName, FileNode node) { + if (appender.containsKey(parentName)) { + appender.get(parentName).getChildList().add(node); + Collections.sort(appender.get(parentName).getChildList(), sortComparator); +// appender.get(parentName).getChildList().sort((final FileNode h1, final FileNode h2) -> h1.getOriginName().compareTo(h2.getOriginName()));//排序 + }else { // 根节点 + FileNode nodeRoot = new FileNode(parentName, parentName, "", new ArrayList<>(), true); + nodeRoot.getChildList().add(node); + appender.put("", nodeRoot); + appender.put(parentName, nodeRoot); + } + } + + private List sortedHeaders(List headers) { + List sortedHeaders = new ArrayList<>(); + Map mapHeaders = new TreeMap<>(); + headers.forEach(header -> mapHeaders.put(header.getFileNameW().length(), header)); + for (Map.Entry entry : mapHeaders.entrySet()){ + for (FileHeader header : headers) { + if (entry.getKey().intValue() == header.getFileNameW().length()) { + sortedHeaders.add(header); + } + } + } + return sortedHeaders; + } + + /** + * 获取倒数第二个文件(夹)名 + * @param fullName + * @param seperator + * 压缩文件解压后,不同的压缩格式分隔符不一样zip是/,而rar是\ + * @param rootName + * 根目录名:如果倒数第二个路径为空,那么赋值为rootName + * @return + */ + 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 (null != leftPath && leftPath.length() > 1) { + // 2.获取倒数第二个 + return getLastFileName(leftPath, seperator); + }else { + return rootName; + } + } + + /** + * 获取最后一个文件(夹)的名字 + * @param fullName + * @param seperator + * 压缩文件解压后,不同的压缩格式分隔符不一样zip是/,而rar是\ + * @return + */ + private static String getLastFileName(String fullName, String seperator) { + if (fullName.endsWith(seperator)) { + fullName = fullName.substring(0, fullName.length()-1); + } + String newName = fullName; + if (null != fullName && fullName.contains(seperator)) { + newName = fullName.substring(fullName.lastIndexOf(seperator) + 1); + } + return newName; + } + + public static Comparator sortComparator = new Comparator() { + 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 class FileNode{ + + private String originName; + private String fileName; + private String parentFileName; + private boolean directory; + private String fileKey;//用于图片预览时寻址 + private List childList; + + public FileNode() { + } + + public FileNode(String originName, String fileName, String parentFileName, List 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 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 getChildList() { + return childList; + } + + public void setChildList(List 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; + } + } + + /** + * Zip文件抽取线程 + */ + class ZipExtractorWorker implements Runnable { + + private List> entriesToBeExtracted; + private ZipFile zipFile; + private String filePath; + + public ZipExtractorWorker(List> entriesToBeExtracted, ZipFile zipFile, String filePath) { + this.entriesToBeExtracted = entriesToBeExtracted; + this.zipFile = zipFile; + this.filePath = filePath; + } + + @Override + public void run() { + System.out.println("解析压缩文件开始《《《《《《《《《《《《《《《《《《《《《《《"); + for (Map 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(); + } + System.out.println("解析压缩文件结束《《《《《《《《《《《《《《《《《《《《《《《"); + } + + + /** + * 读取压缩文件并写入到fileDir文件夹下 + * @param childName + * @param zipFile + */ + 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 (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * Rar文件抽取 + */ + class RarExtractorWorker implements Runnable { + private List> headersToBeExtracted; + private Archive archive; + /** + * 用以删除源文件 + */ + private String filePath; + + public RarExtractorWorker(List> headersToBeExtracted, Archive archive, String filePath) { + this.headersToBeExtracted = headersToBeExtracted; + this.archive = archive; + this.filePath = filePath; + } + + @Override + public void run() { + System.out.println("解析压缩文件开始《《《《《《《《《《《《《《《《《《《《《《《"); + for (Map 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(); + } + System.out.println("解析压缩文件结束《《《《《《《《《《《《《《《《《《《《《《《"); + } + + /** + * 抽取rar文件到指定目录下 + * @param childName + * @param header + * @param archive + */ + private void extractRarFile(String childName, FileHeader header, Archive archive) { + String outPath = fileDir + childName; + try(OutputStream ot = new FileOutputStream(outPath)) { + archive.extractFile(header, ot); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (RarException e) { + e.printStackTrace(); + } + } + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/web/controller/FileController.java b/jodconverter-web/src/main/java/cn/keking/web/controller/FileController.java new file mode 100644 index 00000000..bbab5b54 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/web/controller/FileController.java @@ -0,0 +1,113 @@ +package cn.keking.web.controller; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import cn.keking.param.ReturnResponse; +import cn.keking.utils.FileUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +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 javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +/** + * + * @author yudian-it + * @date 2017/12/1 + */ +@RestController +public class FileController { + @Value("${file.dir}") + String fileDir; + @Autowired + FileUtils fileUtils; + String demoDir = "demo"; + String demoPath = demoDir + File.separator; + + @RequestMapping(value = "fileUpload", method = RequestMethod.POST) + public String fileUpload(@RequestParam("file") MultipartFile file, + HttpServletRequest request) throws JsonProcessingException { + String fileName = file.getOriginalFilename(); + // 判断该文件类型是否有上传过,如果上传过则提示不允许再次上传 + if (existsTypeFile(fileName)) { + return new ObjectMapper().writeValueAsString(new ReturnResponse(1, "每一种类型只可以上传一个文件,请先删除原有文件再次上传", null)); + } + File outFile = new File(fileDir + demoPath); + if (!outFile.exists()) { + outFile.mkdirs(); + } + try(InputStream in = file.getInputStream(); + OutputStream ot = new FileOutputStream(fileDir + demoPath + fileName)){ + byte[] buffer = new byte[1024]; + int len; + while ((-1 != (len = in.read(buffer)))) { + ot.write(buffer, 0, len); + } + return new ObjectMapper().writeValueAsString(new ReturnResponse(0, "SUCCESS", null)); + } catch (IOException e) { + e.printStackTrace(); + return new ObjectMapper().writeValueAsString(new ReturnResponse(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); + if (file.exists()) { + file.delete(); + } + return new ObjectMapper().writeValueAsString(new ReturnResponse(0, "SUCCESS", null)); + } + + @RequestMapping(value = "listFiles", method = RequestMethod.GET) + public String getFiles() throws JsonProcessingException { + List> list = Lists.newArrayList(); + File file = new File(fileDir + demoPath); + if (file.exists()) { + Arrays.stream(file.listFiles()).forEach(file1 -> list.add(ImmutableMap.of("fileName", demoDir + "/" + file1.getName()))); + } + return new ObjectMapper().writeValueAsString(list); + } + + private String getFileName(String name) { + String suffix = name.substring(name.lastIndexOf(".")); + String nameNoSuffix = name.substring(0, name.lastIndexOf(".")); + String uuid = UUID.randomUUID().toString(); + return uuid + "-" + nameNoSuffix + suffix; + } + + /** + * 是否存在该类型的文件 + * @return + * @param fileName + */ + private boolean existsTypeFile(String fileName) { + boolean result = false; + String suffix = fileUtils.getSuffixFromFileName(fileName); + File file = new File(fileDir + demoPath); + if (file.exists()) { + for(File file1 : file.listFiles()){ + String existsFileSuffix = fileUtils.getSuffixFromFileName(file1.getName()); + if (suffix.equals(existsFileSuffix)) { + result = true; + break; + } + } + } + return result; + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/web/controller/IndexController.java b/jodconverter-web/src/main/java/cn/keking/web/controller/IndexController.java new file mode 100644 index 00000000..cb08197b --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/web/controller/IndexController.java @@ -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"; + } +} diff --git a/jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java b/jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java new file mode 100644 index 00000000..318e51b6 --- /dev/null +++ b/jodconverter-web/src/main/java/cn/keking/web/controller/OnlinePreviewController.java @@ -0,0 +1,209 @@ +package cn.keking.web.controller; + +import com.google.common.collect.Lists; +import cn.keking.param.ReturnResponse; +import cn.keking.utils.*; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * @author yudian-it + */ +@Controller +public class OnlinePreviewController { + @Autowired + private OfficeToPdf officeToPdf; + @Autowired + FileUtils fileUtils; + @Autowired + DownloadUtils downloadUtils; + @Autowired + ZipReader zipReader; + @Autowired + SimTextUtil simTextUtil; + @Value("${file.dir}") + String fileDir; + + + /** + * @param url + * @param model + * @return + */ + @RequestMapping(value = "onlinePreview", method = RequestMethod.GET) + public String onlinePreview(String url, Model model, HttpServletRequest req) throws UnsupportedEncodingException { + // 路径转码 + String decodedUrl = URLDecoder.decode(url, "utf-8"); + String type = fileUtils.typeFromUrl(url); + String suffix = suffixFromUrl(url); + // 抽取文件并返回文件列表 + String fileName = fileUtils.getFileNameFromURL(decodedUrl); + model.addAttribute("fileType", suffix); + if (type.equalsIgnoreCase("picture")) { + List imgUrls = Lists.newArrayList(url); + try{ + String fileKey = req.getParameter("fileKey"); + imgUrls.clear(); + imgUrls.addAll(fileUtils.getRedisImgUrls(fileKey)); + }catch (Exception e){ + imgUrls = Lists.newArrayList(url); + } + model.addAttribute("imgurls", imgUrls); + model.addAttribute("currentUrl",url); + return "picture"; + } else if (type.equalsIgnoreCase("simText")) { + ReturnResponse response = simTextUtil.readSimText(decodedUrl, fileName); + if (0 != response.getCode()) { + model.addAttribute("msg", response.getMsg()); + return "fileNotSupported"; + } + model.addAttribute("ordinaryUrl", response.getMsg()); + return "txt"; + } else if (type.equalsIgnoreCase("pdf")) { + model.addAttribute("pdfUrl", url); + return "pdf"; + } else if (type.equalsIgnoreCase("compress")) { + String fileTree = null; + // 判断文件名是否存在(redis缓存读取) + if (!StringUtils.hasText(fileUtils.getConvertedFile(fileName))) { + ReturnResponse response = downloadUtils.downLoad(decodedUrl, suffix, fileName); + if (0 != response.getCode()) { + 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); + } + fileUtils.addConvertedFile(fileName, fileTree); + } else { + fileTree = fileUtils.getConvertedFile(fileName); + } + if (null != fileTree) { + model.addAttribute("fileTree", fileTree); + return "compress"; + } else { + model.addAttribute("msg", "压缩文件类型不受支持,尝试在压缩的时候选择RAR4格式"); + return "fileNotSupported"; + } + } else if ("office".equalsIgnoreCase(type)) { + boolean isHtml = suffix.equalsIgnoreCase("xls") + || suffix.equalsIgnoreCase("xlsx"); + String pdfName = fileName.substring(0, fileName.lastIndexOf(".") + 1) + (isHtml ? "html" : "pdf"); + // 判断之前是否已转换过,如果转换过,直接返回,否则执行转换 + if (!fileUtils.listConvertedFiles().containsKey(pdfName)) { + String filePath = fileDir + fileName; + if (!new File(filePath).exists()) { + ReturnResponse response = downloadUtils.downLoad(decodedUrl, suffix, null); + if (0 != response.getCode()) { + model.addAttribute("msg", response.getMsg()); + return "fileNotSupported"; + } + filePath = response.getContent(); + } + String outFilePath = fileDir + pdfName; + if (StringUtils.hasText(outFilePath)) { + officeToPdf.openOfficeToPDF(filePath, outFilePath); + File f = new File(filePath); + if (f.exists()) { + f.delete(); + } + if (isHtml) { + // 对转换后的文件进行操作(改变编码方式) + fileUtils.doActionConvertedFile(outFilePath); + } + // 加入缓存 + fileUtils.addConvertedFile(pdfName, fileUtils.getRelativePath(outFilePath)); + } + } + model.addAttribute("pdfUrl", pdfName); + return isHtml ? "html" : "pdf"; + } else { + model.addAttribute("msg", "系统还不支持该格式文件的在线预览," + + "如有需要请按下方显示的邮箱地址联系系统维护人员"); + return "fileNotSupported"; + } + } + + /** + * 多图片切换预览 + * + * @param model + * @param req + * @return + * @throws UnsupportedEncodingException + */ + @RequestMapping(value = "picturesPreview", method = RequestMethod.GET) + public String picturesPreview(String urls, Model model, HttpServletRequest req) throws UnsupportedEncodingException { + // 路径转码 + String decodedUrl = URLDecoder.decode(urls, "utf-8"); + // 抽取文件并返回文件列表 + String[] imgs = decodedUrl.split("|"); + List imgurls = Arrays.asList(imgs); + model.addAttribute("imgurls", imgurls); + return "picture"; + } + + + private String suffixFromUrl(String url) { + String nonPramStr = url.substring(0, url.indexOf("?") != -1 ? url.indexOf("?") : url.length()); + String fileName = nonPramStr.substring(nonPramStr.lastIndexOf("/") + 1); + String fileType = fileName.substring(fileName.lastIndexOf(".") + 1); + return fileType; + } + + /** + * 根据url获取文件内容 + * 当pdfjs读取存在跨域问题的文件时将通过此接口读取 + * + * @param urlPath + * @param resp + */ + @RequestMapping(value = "/getCorsFile", method = RequestMethod.GET) + public void getCorsFile(String urlPath, HttpServletResponse resp) { + InputStream inputStream = null; + try { + String strUrl = urlPath.trim(); + URL url = new URL(strUrl); + //打开请求连接 + URLConnection connection = url.openConnection(); + HttpURLConnection httpURLConnection = (HttpURLConnection) connection; + httpURLConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); + inputStream = httpURLConnection.getInputStream(); + byte[] bs = new byte[1024]; + int len; + while (-1 != (len = inputStream.read(bs))) { + resp.getOutputStream().write(bs, 0, len); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (inputStream != null) { + IOUtils.closeQuietly(inputStream); + } + } + } + +} diff --git a/jodconverter-web/src/test/java/com/yudianbank/FilePreviewApplicationTests.java b/jodconverter-web/src/test/java/com/yudianbank/FilePreviewApplicationTests.java index d92b6245..535f6564 100644 --- a/jodconverter-web/src/test/java/com/yudianbank/FilePreviewApplicationTests.java +++ b/jodconverter-web/src/test/java/com/yudianbank/FilePreviewApplicationTests.java @@ -1,4 +1,4 @@ -package com.yudianbank; +package cn.keking; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/pom.xml b/pom.xml index 03468e2b..0a3b1e67 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.yudianbank + cn.keking filepreview 0.0.1-SNAPSHOT