diff --git a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java index 48442bb4f..04f8c5b26 100644 --- a/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/io/FileUtil.java @@ -306,6 +306,7 @@ public class FileUtil extends PathUtil { /** * 创建File对象
+ * 根据的路径构建文件,在Win下直接构建,在Linux下拆分路径单独构建 * 此方法会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/ * * @param parent 父文件对象 @@ -316,7 +317,7 @@ public class FileUtil extends PathUtil { if (StrUtil.isBlank(path)) { throw new NullPointerException("File path is blank!"); } - return checkSlip(parent, new File(parent, path)); + return checkSlip(parent, buildFile(parent, path)); } /** @@ -3277,4 +3278,34 @@ public class FileUtil extends PathUtil { public static void tail(File file, Charset charset) { tail(file, charset, Tailer.CONSOLE_HANDLER); } + + /** + * 根据压缩包中的路径构建目录结构,在Win下直接构建,在Linux下拆分路径单独构建 + * + * @param outFile 最外部路径 + * @param fileName 文件名,可以包含路径 + * @return 文件或目录 + * @since 5.0.5 + */ + private static File buildFile(File outFile, String fileName) { + // 替换Windows路径分隔符为Linux路径分隔符,便于统一处理 + fileName = fileName.replace('\\', '/'); + if (false == FileUtil.isWindows() + // 检查文件名中是否包含"/",不考虑以"/"结尾的情况 + && fileName.lastIndexOf(CharUtil.SLASH, fileName.length() - 2) > 0) { + // 在Linux下多层目录创建存在问题,/会被当成文件名的一部分,此处做处理 + // 使用/拆分路径(zip中无\),级联创建父目录 + final List pathParts = StrUtil.split(fileName, '/', false, true); + final int lastPartIndex = pathParts.size() - 1;//目录个数 + for (int i = 0; i < lastPartIndex; i++) { + //由于路径拆分,slip不检查,在最后一步检查 + outFile = new File(outFile, pathParts.get(i)); + } + //noinspection ResultOfMethodCallIgnored + outFile.mkdirs(); + // 最后一个部分如果非空,作为文件名 + fileName = pathParts.get(lastPartIndex); + } + return new File(outFile, fileName); + } } diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java index a78212d1c..a8d025c5d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ZipUtil.java @@ -471,7 +471,7 @@ public class ZipUtil { while (em.hasMoreElements()) { zipEntry = em.nextElement(); // FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/ - outItemFile = buildFile(outFile, zipEntry.getName()); + outItemFile = FileUtil.file(outFile, zipEntry.getName()); if (zipEntry.isDirectory()) { // 创建对应目录 //noinspection ResultOfMethodCallIgnored @@ -1106,36 +1106,6 @@ public class ZipUtil { throw new IORuntimeException(e); } } - - /** - * 根据压缩包中的路径构建目录结构,在Win下直接构建,在Linux下拆分路径单独构建 - * - * @param outFile 最外部路径 - * @param fileName 文件名,可以包含路径 - * @return 文件或目录 - * @since 5.0.5 - */ - private static File buildFile(File outFile, String fileName) { - // 替换Windows路径分隔符为Linux路径分隔符,便于统一处理 - fileName = fileName.replace('\\', '/'); - if (false == FileUtil.isWindows() - // 检查文件名中是否包含"/",不考虑以"/"结尾的情况 - && fileName.lastIndexOf(CharUtil.SLASH, fileName.length() - 2) > 0) { - // 在Linux下多层目录创建存在问题,/会被当成文件名的一部分,此处做处理 - // 使用/拆分路径(zip中无\),级联创建父目录 - final List pathParts = StrUtil.split(fileName, '/', false, true); - final int lastPartIndex = pathParts.size() - 1;//目录个数 - for (int i = 0; i < lastPartIndex; i++) { - //由于路径拆分,slip不检查,在最后一步检查 - outFile = new File(outFile, pathParts.get(i)); - } - //noinspection ResultOfMethodCallIgnored - outFile.mkdirs(); - // 最后一个部分如果非空,作为文件名 - fileName = pathParts.get(lastPartIndex); - } - return FileUtil.file(outFile, fileName); - } // ---------------------------------------------------------------------------------------------- Private method end } \ No newline at end of file diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/Extractor.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/Extractor.java deleted file mode 100644 index 7819de07f..000000000 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/Extractor.java +++ /dev/null @@ -1,4 +0,0 @@ -package cn.hutool.extra.compress; - -public class Extractor { -} diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/Archiver.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/Archiver.java index c13f3a4e7..e91117296 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/Archiver.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/Archiver.java @@ -7,7 +7,7 @@ import java.io.File; import java.io.FileFilter; /** - * 数据归档封装,归档即将几个文件或目录打成一个压缩包
+ * 数据归档封装,归档即将几个文件或目录打成一个压缩包 * * @author looly */ diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/StreamArchiver.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/StreamArchiver.java index fb2feac82..6c01b8fe0 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/StreamArchiver.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/StreamArchiver.java @@ -56,7 +56,7 @@ public class StreamArchiver implements Archiver { return new StreamArchiver(charset, archiverName, out); } - private ArchiveOutputStream out; + private final ArchiveOutputStream out; /** * 构造 diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/package-info.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/package-info.java new file mode 100644 index 000000000..097f9a385 --- /dev/null +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/package-info.java @@ -0,0 +1,11 @@ +/** + * 基于commons-compress的打包(压缩)封装 + * + *

+ * 见:https://commons.apache.org/proper/commons-compress/ + *

+ * + * @author looly + * + */ +package cn.hutool.extra.compress.archiver; \ No newline at end of file diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Extractor.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Extractor.java new file mode 100644 index 000000000..2ff894107 --- /dev/null +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Extractor.java @@ -0,0 +1,11 @@ +package cn.hutool.extra.compress.extractor; + +/** + * 数据解包封装,用于将zip、tar等包解包为文件 + * + * @author looly + * @since 5.5.0 + */ +public interface Extractor { + +} diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/StreamExtractor.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/StreamExtractor.java new file mode 100644 index 000000000..338417d34 --- /dev/null +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/StreamExtractor.java @@ -0,0 +1,69 @@ +package cn.hutool.extra.compress.extractor; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.lang.Assert; +import cn.hutool.extra.compress.CompressException; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveException; +import org.apache.commons.compress.archivers.ArchiveInputStream; +import org.apache.commons.compress.archivers.ArchiveStreamFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + +public class StreamExtractor { + + private final ArchiveInputStream in; + + public StreamExtractor(Charset charset, InputStream in) { + final ArchiveStreamFactory factory = new ArchiveStreamFactory(charset.name()); + try { + this.in = factory.createArchiveInputStream(in); + } catch (ArchiveException e) { + throw new CompressException(e); + } + } + + /** + * 释放(解压)到指定目录 + * + * @param targetDir 目标目录 + */ + public void extract(File targetDir) { + try { + extractInternal(targetDir); + } catch (IOException e) { + throw new IORuntimeException(e); + } + } + + /** + * 释放(解压)到指定目录 + * + * @param targetDir 目标目录 + * @throws IOException IO异常 + */ + private void extractInternal(File targetDir) throws IOException { + Assert.isTrue(null != targetDir && targetDir.isDirectory(), "target must be dir."); + final ArchiveInputStream in = this.in; + ArchiveEntry entry; + File outItemFile; + while(null != (entry = this.in.getNextEntry())){ + if(false == in.canReadEntryData(entry)){ + // 无法读取的文件直接跳过 + continue; + } + outItemFile = FileUtil.file(targetDir, entry.getName()); + if(entry.isDirectory()){ + // 创建对应目录 + //noinspection ResultOfMethodCallIgnored + outItemFile.mkdirs(); + } else { + FileUtil.writeFromStream(in, outItemFile); + } + } + } +} diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/package-info.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/package-info.java new file mode 100644 index 000000000..676207e15 --- /dev/null +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/package-info.java @@ -0,0 +1,11 @@ +/** + * 基于commons-compress的解包(解压缩)封装 + * + *

+ * 见:https://commons.apache.org/proper/commons-compress/ + *

+ * + * @author looly + * + */ +package cn.hutool.extra.compress.extractor; \ No newline at end of file diff --git a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/freemarker/FreemarkerEngine.java b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/freemarker/FreemarkerEngine.java index e46f0f0f5..c36bc7c21 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/freemarker/FreemarkerEngine.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/freemarker/FreemarkerEngine.java @@ -1,7 +1,5 @@ package cn.hutool.extra.template.engine.freemarker; -import java.io.IOException; - import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.util.ClassUtil; @@ -13,9 +11,12 @@ import freemarker.cache.ClassTemplateLoader; import freemarker.cache.FileTemplateLoader; import freemarker.template.Configuration; +import java.io.IOException; + /** - * Beetl模板引擎封装 - * + * FreeMarker模板引擎封装
+ * 见:https://freemarker.apache.org/ + * * @author looly */ public class FreemarkerEngine implements TemplateEngine { @@ -23,14 +24,16 @@ public class FreemarkerEngine implements TemplateEngine { private Configuration cfg; // --------------------------------------------------------------------------------- Constructor start + /** * 默认构造 */ - public FreemarkerEngine() {} + public FreemarkerEngine() { + } /** * 构造 - * + * * @param config 模板配置 */ public FreemarkerEngine(TemplateConfig config) { @@ -39,7 +42,7 @@ public class FreemarkerEngine implements TemplateEngine { /** * 构造 - * + * * @param freemarkerCfg {@link Configuration} */ public FreemarkerEngine(Configuration freemarkerCfg) { @@ -49,7 +52,7 @@ public class FreemarkerEngine implements TemplateEngine { @Override public TemplateEngine init(TemplateConfig config) { - if(null == config){ + if (null == config) { config = TemplateConfig.DEFAULT; } init(createCfg(config)); @@ -58,29 +61,30 @@ public class FreemarkerEngine implements TemplateEngine { /** * 初始化引擎 + * * @param freemarkerCfg Configuration */ - private void init(Configuration freemarkerCfg){ + private void init(Configuration freemarkerCfg) { this.cfg = freemarkerCfg; } @Override public Template getTemplate(String resource) { - if(null == this.cfg){ + if (null == this.cfg) { init(TemplateConfig.DEFAULT); } try { return FreemarkerTemplate.wrap(this.cfg.getTemplate(resource)); - } catch(IOException e) { + } catch (IOException e) { throw new IORuntimeException(e); - }catch (Exception e) { + } catch (Exception e) { throw new TemplateException(e); } } /** * 创建配置项 - * + * * @param config 模板配置 * @return {@link Configuration } */ @@ -94,30 +98,30 @@ public class FreemarkerEngine implements TemplateEngine { cfg.setDefaultEncoding(config.getCharset().toString()); switch (config.getResourceMode()) { - case CLASSPATH: - cfg.setTemplateLoader(new ClassTemplateLoader(ClassUtil.getClassLoader(), config.getPath())); - break; - case FILE: - try { - cfg.setTemplateLoader(new FileTemplateLoader(FileUtil.file(config.getPath()))); - } catch (IOException e) { - throw new IORuntimeException(e); - } - break; - case WEB_ROOT: - try { - cfg.setTemplateLoader(new FileTemplateLoader(FileUtil.file(FileUtil.getWebRoot(), config.getPath()))); - } catch (IOException e) { - throw new IORuntimeException(e); - } - break; - case STRING: - cfg.setTemplateLoader(new SimpleStringTemplateLoader()); - break; - default: - break; + case CLASSPATH: + cfg.setTemplateLoader(new ClassTemplateLoader(ClassUtil.getClassLoader(), config.getPath())); + break; + case FILE: + try { + cfg.setTemplateLoader(new FileTemplateLoader(FileUtil.file(config.getPath()))); + } catch (IOException e) { + throw new IORuntimeException(e); + } + break; + case WEB_ROOT: + try { + cfg.setTemplateLoader(new FileTemplateLoader(FileUtil.file(FileUtil.getWebRoot(), config.getPath()))); + } catch (IOException e) { + throw new IORuntimeException(e); + } + break; + case STRING: + cfg.setTemplateLoader(new SimpleStringTemplateLoader()); + break; + default: + break; } - + return cfg; } } diff --git a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/freemarker/package-info.java b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/freemarker/package-info.java index f4ac9bb43..a0f328711 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/freemarker/package-info.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/freemarker/package-info.java @@ -1,7 +1,7 @@ /** - * Freemarker实现 - * - * @author looly + * Freemarker实现
+ * 见:https://freemarker.apache.org/ * + * @author looly */ package cn.hutool.extra.template.engine.freemarker; \ No newline at end of file diff --git a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/velocity/VelocityEngine.java b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/velocity/VelocityEngine.java index 73a97efb1..3630b972c 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/velocity/VelocityEngine.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/velocity/VelocityEngine.java @@ -7,10 +7,10 @@ import cn.hutool.extra.template.TemplateEngine; import org.apache.velocity.app.Velocity; /** - * Velocity模板引擎 - * - * @author looly + * Velocity模板引擎
+ * 见:http://velocity.apache.org/ * + * @author looly */ public class VelocityEngine implements TemplateEngine { @@ -18,14 +18,16 @@ public class VelocityEngine implements TemplateEngine { private TemplateConfig config; // --------------------------------------------------------------------------------- Constructor start + /** * 默认构造 */ - public VelocityEngine() {} + public VelocityEngine() { + } /** * 构造 - * + * * @param config 模板配置 */ public VelocityEngine(TemplateConfig config) { @@ -34,7 +36,7 @@ public class VelocityEngine implements TemplateEngine { /** * 构造 - * + * * @param engine {@link org.apache.velocity.app.VelocityEngine} */ public VelocityEngine(org.apache.velocity.app.VelocityEngine engine) { @@ -44,7 +46,7 @@ public class VelocityEngine implements TemplateEngine { @Override public TemplateEngine init(TemplateConfig config) { - if(null == config){ + if (null == config) { config = TemplateConfig.DEFAULT; } this.config = config; @@ -54,15 +56,16 @@ public class VelocityEngine implements TemplateEngine { /** * 初始化引擎 + * * @param engine 引擎 */ - private void init(org.apache.velocity.app.VelocityEngine engine){ + private void init(org.apache.velocity.app.VelocityEngine engine) { this.engine = engine; } /** * 获取原始的引擎对象 - * + * * @return 原始引擎对象 * @since 4.3.0 */ @@ -72,7 +75,7 @@ public class VelocityEngine implements TemplateEngine { @Override public Template getTemplate(String resource) { - if(null == this.engine){ + if (null == this.engine) { init(TemplateConfig.DEFAULT); } @@ -80,15 +83,15 @@ public class VelocityEngine implements TemplateEngine { String root; // 自定义编码 String charsetStr = null; - if(null != this.config){ + if (null != this.config) { root = this.config.getPath(); charsetStr = this.config.getCharsetStr(); // 修正template目录,在classpath或者web_root模式下,按照配置添加默认前缀 // 如果用户已经自行添加了前缀,则忽略之 final TemplateConfig.ResourceMode resourceMode = this.config.getResourceMode(); - if(TemplateConfig.ResourceMode.CLASSPATH == resourceMode - || TemplateConfig.ResourceMode.WEB_ROOT == resourceMode){ + if (TemplateConfig.ResourceMode.CLASSPATH == resourceMode + || TemplateConfig.ResourceMode.WEB_ROOT == resourceMode) { resource = StrUtil.addPrefixIfNot(resource, StrUtil.addSuffixIfNot(root, "/")); } } @@ -98,7 +101,7 @@ public class VelocityEngine implements TemplateEngine { /** * 创建引擎 - * + * * @param config 模板配置 * @return {@link org.apache.velocity.app.VelocityEngine} */ @@ -116,29 +119,29 @@ public class VelocityEngine implements TemplateEngine { // loader switch (config.getResourceMode()) { - case CLASSPATH: - // 新版Velocity弃用 + case CLASSPATH: + // 新版Velocity弃用 // ve.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); - ve.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); - break; - case FILE: - // path - final String path = config.getPath(); - if (null != path) { - ve.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, path); - } - break; - case WEB_ROOT: - ve.setProperty(Velocity.RESOURCE_LOADER, "webapp"); - ve.setProperty("webapp.resource.loader.class", "org.apache.velocity.tools.view.servlet.WebappLoader"); - ve.setProperty("webapp.resource.loader.path", StrUtil.nullToDefault(config.getPath(), StrUtil.SLASH)); - break; - case STRING: - ve.setProperty(Velocity.RESOURCE_LOADER, "str"); - ve.setProperty("str.resource.loader.class", SimpleStringResourceLoader.class.getName()); - break; - default: - break; + ve.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + break; + case FILE: + // path + final String path = config.getPath(); + if (null != path) { + ve.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, path); + } + break; + case WEB_ROOT: + ve.setProperty(Velocity.RESOURCE_LOADER, "webapp"); + ve.setProperty("webapp.resource.loader.class", "org.apache.velocity.tools.view.servlet.WebappLoader"); + ve.setProperty("webapp.resource.loader.path", StrUtil.nullToDefault(config.getPath(), StrUtil.SLASH)); + break; + case STRING: + ve.setProperty(Velocity.RESOURCE_LOADER, "str"); + ve.setProperty("str.resource.loader.class", SimpleStringResourceLoader.class.getName()); + break; + default: + break; } ve.init(); diff --git a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/velocity/package-info.java b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/velocity/package-info.java index 933ed31da..f6eebce8f 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/template/engine/velocity/package-info.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/template/engine/velocity/package-info.java @@ -1,7 +1,7 @@ /** - * Velocity实现 - * - * @author looly + * Velocity实现
+ * 见:http://velocity.apache.org/ * + * @author looly */ package cn.hutool.extra.template.engine.velocity; \ No newline at end of file