mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add compiler
This commit is contained in:
parent
9857ac4484
commit
b2ee5fbfec
@ -24,6 +24,8 @@
|
||||
* 【cache 】 增加CacheListener(issue#1257@Github)
|
||||
* 【core 】 TimeInterval支持分组(issue#1238@Github)
|
||||
* 【core 】 增加compile包(pr#1243@Github)
|
||||
* 【core 】 增加ResourceClassLoader、CharSequenceResource、FileObjectResource
|
||||
* 【core 】 修改IoUtil.read(Reader)逻辑默认关闭Reader
|
||||
|
||||
### Bug修复
|
||||
* 【cron 】 修复CronTimer可能死循环的问题(issue#1224@Github)
|
||||
|
@ -0,0 +1,34 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 编译异常
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class CompilerException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CompilerException(Throwable e) {
|
||||
super(ExceptionUtil.getMessage(e), e);
|
||||
}
|
||||
|
||||
public CompilerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CompilerException(String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
public CompilerException(String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
public CompilerException(Throwable throwable, String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import javax.tools.DiagnosticListener;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
/**
|
||||
* 源码编译工具类,主要封装{@link JavaCompiler} 相关功能
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class CompilerUtil {
|
||||
/**
|
||||
* java 编译器
|
||||
*/
|
||||
public static final JavaCompiler SYSTEM_COMPILER = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
/**
|
||||
* 编译指定的源码文件
|
||||
*
|
||||
* @param sourceFiles 源码文件路径
|
||||
* @return 0表示成功,否则其他
|
||||
*/
|
||||
public static boolean compile(String... sourceFiles) {
|
||||
return 0 == SYSTEM_COMPILER.run(null, null, null, sourceFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link JavaFileManager}
|
||||
*
|
||||
* @return {@link JavaFileManager}
|
||||
*/
|
||||
public static JavaFileManager getFileManager() {
|
||||
return SYSTEM_COMPILER.getStandardFileManager(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新建编译任务
|
||||
*
|
||||
* @param fileManager {@link JavaFileManager},用于管理已经编译好的文件
|
||||
* @param diagnosticListener 诊断监听
|
||||
* @param options 选项,例如 -cpXXX等
|
||||
* @param compilationUnits 编译单元,即需要编译的对象
|
||||
* @return {@link JavaCompiler.CompilationTask}
|
||||
*/
|
||||
public static JavaCompiler.CompilationTask getTask(
|
||||
JavaFileManager fileManager,
|
||||
DiagnosticListener<? super JavaFileObject> diagnosticListener,
|
||||
Iterable<String> options,
|
||||
Iterable<? extends JavaFileObject> compilationUnits) {
|
||||
return SYSTEM_COMPILER.getTask(null, fileManager, diagnosticListener, options, null, compilationUnits);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 诊断工具类
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class DiagnosticUtil {
|
||||
|
||||
/**
|
||||
* 获取{@link DiagnosticCollector}收集到的诊断信息,以文本返回
|
||||
*
|
||||
* @param collector {@link DiagnosticCollector}
|
||||
* @return 诊断消息
|
||||
*/
|
||||
public static String getMessages(DiagnosticCollector<?> collector) {
|
||||
final List<?> diagnostics = collector.getDiagnostics();
|
||||
return diagnostics.stream().map(String::valueOf)
|
||||
.collect(Collectors.joining(System.lineSeparator()));
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.FileObjectResource;
|
||||
import cn.hutool.core.lang.ResourceClassLoader;
|
||||
import cn.hutool.core.util.ClassLoaderUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
@ -10,16 +10,16 @@ import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.JavaFileObject.Kind;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.SecureClassLoader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Java 字节码文件对象
|
||||
* Java 字节码文件对象管理器
|
||||
*
|
||||
* <p>
|
||||
* 正常我们使用javac命令编译源码时会将class文件写入到磁盘中,但在运行时动态编译类不适合保存在磁盘中
|
||||
* 我们采取此对象来管理运行时动态编译类生成的字节码
|
||||
* 我们采取此对象来管理运行时动态编译类生成的字节码。
|
||||
* </p>
|
||||
*
|
||||
* @author lzpeng
|
||||
* @since 5.5.2
|
||||
@ -29,7 +29,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
/**
|
||||
* 存储java字节码文件对象映射
|
||||
*/
|
||||
private final Map<String, JavaFileObject> javaFileObjectMap = new HashMap<>();
|
||||
private final Map<String, FileObjectResource> classFileObjectMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 加载动态编译生成类的父类加载器
|
||||
@ -55,28 +55,7 @@ class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
*/
|
||||
@Override
|
||||
public ClassLoader getClassLoader(final Location location) {
|
||||
return new SecureClassLoader(parent) {
|
||||
|
||||
/**
|
||||
* 查找类
|
||||
* @param name 类名
|
||||
* @return 类的class对象
|
||||
* @throws ClassNotFoundException 未找到类异常
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> findClass(final String name) throws ClassNotFoundException {
|
||||
final JavaFileObject javaFileObject = javaFileObjectMap.get(name);
|
||||
if (null != javaFileObject) {
|
||||
try(final InputStream inputStream = javaFileObject.openInputStream()){
|
||||
final byte[] bytes = IoUtil.readBytes(inputStream);
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
};
|
||||
return new ResourceClassLoader<>(this.parent, this.classFileObjectMap);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,13 +65,13 @@ class JavaClassFileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
* @param location 源码位置
|
||||
* @param className 类名
|
||||
* @param kind 文件类型
|
||||
* @param sibling 将Java源码对象
|
||||
* @param sibling Java源码对象
|
||||
* @return Java字节码文件对象
|
||||
*/
|
||||
@Override
|
||||
public JavaFileObject getJavaFileForOutput(final Location location, final String className, final Kind kind, final FileObject sibling) {
|
||||
final JavaFileObject javaFileObject = new JavaClassFileObject(className, kind);
|
||||
javaFileObjectMap.put(className, javaFileObject);
|
||||
this.classFileObjectMap.put(className, new FileObjectResource(javaFileObject));
|
||||
return javaFileObject;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,75 @@
|
||||
package cn.hutool.core.compiler;
|
||||
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
|
||||
import javax.tools.JavaFileObject;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* {@link JavaFileObject} 相关工具类封装
|
||||
*
|
||||
* @author lzpeng, looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class JavaFileObjectUtil {
|
||||
|
||||
/**
|
||||
* 获取指定文件下的所有待编译的java文件,并以{@link JavaFileObject}形式返回
|
||||
*
|
||||
* @param file 文件或目录,文件支持.java、.jar和.zip文件
|
||||
* @return 所有待编译的 {@link JavaFileObject}
|
||||
*/
|
||||
public static List<JavaFileObject> getJavaFileObjects(File file) {
|
||||
final List<JavaFileObject> result = new ArrayList<>();
|
||||
final String fileName = file.getName();
|
||||
|
||||
if (isJavaFile(fileName)) {
|
||||
result.add(new JavaSourceFileObject(file.toURI()));
|
||||
} else if (isJarOrZipFile(fileName)) {
|
||||
result.addAll(getJavaFileObjectByZipOrJarFile(file));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是jar 或 zip 文件
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 是否是jar 或 zip 文件
|
||||
*/
|
||||
public static boolean isJarOrZipFile(String fileName) {
|
||||
return FileNameUtil.isType(fileName, "jar", "zip");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是java文件
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return 是否是.java文件
|
||||
*/
|
||||
public static boolean isJavaFile(String fileName) {
|
||||
return FileNameUtil.isType(fileName, "java");
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过zip包或jar包创建Java文件对象
|
||||
*
|
||||
* @param file 压缩文件
|
||||
* @return Java文件对象
|
||||
*/
|
||||
private static List<JavaFileObject> getJavaFileObjectByZipOrJarFile(File file) {
|
||||
final List<JavaFileObject> collection = new ArrayList<>();
|
||||
final ZipFile zipFile = ZipUtil.toZipFile(file, null);
|
||||
ZipUtil.read(zipFile, (zipEntry) -> {
|
||||
final String name = zipEntry.getName();
|
||||
if (isJavaFile(name)) {
|
||||
collection.add(new JavaSourceFileObject(name, ZipUtil.getStream(zipFile, zipEntry)));
|
||||
}
|
||||
});
|
||||
return collection;
|
||||
}
|
||||
}
|
@ -9,28 +9,21 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaCompiler.CompilationTask;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Java 源码编译器
|
||||
@ -39,11 +32,6 @@ import java.util.zip.ZipFile;
|
||||
*/
|
||||
public class JavaSourceCompiler {
|
||||
|
||||
/**
|
||||
* java 编译器
|
||||
*/
|
||||
private static final JavaCompiler JAVA_COMPILER = ToolProvider.getSystemJavaCompiler();
|
||||
|
||||
/**
|
||||
* 待编译的文件 可以是 .java文件 压缩文件 文件夹 递归搜索文件夹内的zip包和jar包
|
||||
*/
|
||||
@ -68,10 +56,10 @@ public class JavaSourceCompiler {
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param parent 父类加载器
|
||||
* @param parent 父类加载器,null则使用默认类加载器
|
||||
*/
|
||||
private JavaSourceCompiler(ClassLoader parent) {
|
||||
this.parentClassLoader = parent;
|
||||
this.parentClassLoader = ObjectUtil.defaultIfNull(parent, ClassLoaderUtil.getClassLoader());
|
||||
}
|
||||
|
||||
|
||||
@ -145,22 +133,19 @@ public class JavaSourceCompiler {
|
||||
* @return 类加载器
|
||||
*/
|
||||
public ClassLoader compile() {
|
||||
final ClassLoader parent = ObjectUtil.defaultIfNull(this.parentClassLoader, ClassLoaderUtil.getClassLoader());
|
||||
|
||||
// 获得classPath
|
||||
final List<File> classPath = getClassPath();
|
||||
final URL[] urLs = URLUtil.getURLs(classPath.toArray(new File[0]));
|
||||
final URLClassLoader ucl = URLClassLoader.newInstance(urLs, parent);
|
||||
final URLClassLoader ucl = URLClassLoader.newInstance(urLs, this.parentClassLoader);
|
||||
if (sourceCodeMap.isEmpty() && sourceFileList.isEmpty()) {
|
||||
// 没有需要编译的源码
|
||||
return ucl;
|
||||
}
|
||||
|
||||
// 没有需要编译的源码文件返回加载zip或jar包的类加载器
|
||||
final Iterable<JavaFileObject> javaFileObjectList = getJavaFileObject();
|
||||
|
||||
// 创建编译器
|
||||
final JavaFileManager standardJavaFileManager = JAVA_COMPILER.getStandardFileManager(null, null, null);
|
||||
final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, standardJavaFileManager);
|
||||
final JavaFileManager javaFileManager = new JavaClassFileManager(ucl, CompilerUtil.getFileManager());
|
||||
|
||||
// classpath
|
||||
final List<String> options = new ArrayList<>();
|
||||
@ -172,17 +157,14 @@ public class JavaSourceCompiler {
|
||||
|
||||
// 编译文件
|
||||
final DiagnosticCollector<? super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
|
||||
final CompilationTask task = JAVA_COMPILER.getTask(null, javaFileManager, diagnosticCollector,
|
||||
options, null, javaFileObjectList);
|
||||
final List<JavaFileObject> javaFileObjectList = getJavaFileObject();
|
||||
final CompilationTask task = CompilerUtil.getTask(javaFileManager, diagnosticCollector, options, javaFileObjectList);
|
||||
if (task.call()) {
|
||||
// 加载编译后的类
|
||||
return javaFileManager.getClassLoader(StandardLocation.CLASS_OUTPUT);
|
||||
} else {
|
||||
// 编译失败,收集错误信息
|
||||
final List<?> diagnostics = diagnosticCollector.getDiagnostics();
|
||||
final String errorMsg = diagnostics.stream().map(String::valueOf)
|
||||
.collect(Collectors.joining(System.lineSeparator()));
|
||||
// CompileException
|
||||
throw new RuntimeException(errorMsg);
|
||||
throw new CompilerException(DiagnosticUtil.getMessages(diagnosticCollector));
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +176,7 @@ public class JavaSourceCompiler {
|
||||
private List<File> getClassPath() {
|
||||
List<File> classPathFileList = new ArrayList<>();
|
||||
for (File file : libraryFileList) {
|
||||
List<File> jarOrZipFile = FileUtil.loopFiles(file, this::isJarOrZipFile);
|
||||
List<File> jarOrZipFile = FileUtil.loopFiles(file, (subFile)-> JavaFileObjectUtil.isJarOrZipFile(subFile.getName()));
|
||||
classPathFileList.addAll(jarOrZipFile);
|
||||
if (file.isDirectory()) {
|
||||
classPathFileList.add(file);
|
||||
@ -208,20 +190,14 @@ public class JavaSourceCompiler {
|
||||
*
|
||||
* @return 待编译的Java文件对象
|
||||
*/
|
||||
private Iterable<JavaFileObject> getJavaFileObject() {
|
||||
final Collection<JavaFileObject> collection = new ArrayList<>();
|
||||
private List<JavaFileObject> getJavaFileObject() {
|
||||
final List<JavaFileObject> collection = new ArrayList<>();
|
||||
|
||||
// 源码文件
|
||||
for (File file : sourceFileList) {
|
||||
// .java 文件
|
||||
final List<File> javaFileList = FileUtil.loopFiles(file, this::isJavaFile);
|
||||
for (File javaFile : javaFileList) {
|
||||
collection.add(getJavaFileObjectByJavaFile(javaFile));
|
||||
}
|
||||
// 压缩包
|
||||
final List<File> jarOrZipFileList = FileUtil.loopFiles(file, this::isJarOrZipFile);
|
||||
for (File jarOrZipFile : jarOrZipFileList) {
|
||||
collection.addAll(getJavaFileObjectByZipOrJarFile(jarOrZipFile));
|
||||
}
|
||||
FileUtil.walkFiles(file, (subFile)-> collection.addAll(JavaFileObjectUtil.getJavaFileObjects(file)));
|
||||
}
|
||||
|
||||
// 源码Map
|
||||
collection.addAll(getJavaFileObjectByMap(this.sourceCodeMap));
|
||||
return collection;
|
||||
@ -252,54 +228,4 @@ public class JavaSourceCompiler {
|
||||
return new JavaSourceFileObject(file.toURI());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过zip包或jar包创建Java文件对象
|
||||
*
|
||||
* @param file 压缩文件
|
||||
* @return Java文件对象
|
||||
*/
|
||||
private Collection<JavaFileObject> getJavaFileObjectByZipOrJarFile(final File file) {
|
||||
final Collection<JavaFileObject> collection = new ArrayList<>();
|
||||
try {
|
||||
final ZipFile zipFile = new ZipFile(file);
|
||||
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
final ZipEntry zipEntry = entries.nextElement();
|
||||
final String name = zipEntry.getName();
|
||||
if (name.endsWith(".java")) {
|
||||
final InputStream inputStream = zipFile.getInputStream(zipEntry);
|
||||
final JavaSourceFileObject fileObject = new JavaSourceFileObject(name, inputStream);
|
||||
collection.add(fileObject);
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 是否是jar 或 zip 文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 是否是jar 或 zip 文件
|
||||
*/
|
||||
private boolean isJarOrZipFile(final File file) {
|
||||
final String fileName = file.getName();
|
||||
return fileName.endsWith(".jar") || fileName.endsWith(".zip");
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是.java文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 是否是.java文件
|
||||
*/
|
||||
private boolean isJavaFile(final File file) {
|
||||
final String fileName = file.getName();
|
||||
return fileName.endsWith(".java");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,11 @@ import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* Java 源码文件对象
|
||||
* Java 源码文件对象,支持:<br>
|
||||
* <ol>
|
||||
* <li>源文件</li>
|
||||
* <li>代码内容</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author lzpeng
|
||||
* @since 5.5.2
|
||||
@ -34,23 +38,22 @@ class JavaSourceFileObject extends SimpleJavaFileObject {
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param name 需要编译的文件名
|
||||
* @param inputStream 输入流
|
||||
* @param className 需要编译的类名
|
||||
* @param code 需要编译的类源码
|
||||
*/
|
||||
protected JavaSourceFileObject(String name, InputStream inputStream) {
|
||||
this(URI.create("string:///" + name));
|
||||
this.inputStream = inputStream;
|
||||
protected JavaSourceFileObject(String className, String code, Charset charset) {
|
||||
this(className, IoUtil.toStream(code, charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param className 需要编译的类名
|
||||
* @param code 需要编译的类源码
|
||||
* @param name 需要编译的文件名
|
||||
* @param inputStream 输入流
|
||||
*/
|
||||
protected JavaSourceFileObject(String className, String code, Charset charset) {
|
||||
this(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension));
|
||||
this.inputStream = IoUtil.toStream(code, charset);
|
||||
protected JavaSourceFileObject(String name, InputStream inputStream) {
|
||||
this(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension));
|
||||
this.inputStream = inputStream;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.hutool.core.io;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.io.file.FileCopier;
|
||||
import cn.hutool.core.io.file.FileMode;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
@ -50,6 +51,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Checksum;
|
||||
@ -64,11 +66,11 @@ public class FileUtil extends PathUtil {
|
||||
/**
|
||||
* Class文件扩展名
|
||||
*/
|
||||
public static final String CLASS_EXT = ".class";
|
||||
public static final String CLASS_EXT = FileNameUtil.EXT_CLASS;
|
||||
/**
|
||||
* Jar文件扩展名
|
||||
*/
|
||||
public static final String JAR_FILE_EXT = ".jar";
|
||||
public static final String JAR_FILE_EXT = FileNameUtil.EXT_JAR;
|
||||
/**
|
||||
* 在Jar中的路径jar的扩展名形式
|
||||
*/
|
||||
@ -171,25 +173,33 @@ public class FileUtil extends PathUtil {
|
||||
* @return 文件列表
|
||||
*/
|
||||
public static List<File> loopFiles(File file, FileFilter fileFilter) {
|
||||
final List<File> fileList = new ArrayList<>();
|
||||
if (null == file || false == file.exists()) {
|
||||
return fileList;
|
||||
return ListUtil.empty();
|
||||
}
|
||||
|
||||
final List<File> fileList = new ArrayList<>();
|
||||
walkFiles(file, fileList::add);
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归遍历目录并处理目录下的文件
|
||||
*
|
||||
* @param file 文件或目录,文件直接处理
|
||||
* @param consumer 文件处理器,只会处理文件
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static void walkFiles(File file, Consumer<File> consumer) {
|
||||
if (file.isDirectory()) {
|
||||
final File[] subFiles = file.listFiles();
|
||||
if (ArrayUtil.isNotEmpty(subFiles)) {
|
||||
for (File tmp : subFiles) {
|
||||
fileList.addAll(loopFiles(tmp, fileFilter));
|
||||
walkFiles(tmp, consumer);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (null == fileFilter || fileFilter.accept(file)) {
|
||||
fileList.add(file);
|
||||
}
|
||||
consumer.accept(file);
|
||||
}
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -968,7 +978,7 @@ public class FileUtil extends PathUtil {
|
||||
* 移动文件或者目录
|
||||
*
|
||||
* @param src 源文件或者目录
|
||||
* @param target 目标文件或者目录
|
||||
* @param target 目标文件或者目录
|
||||
* @param isOverride 是否覆盖目标,只有目标为文件才覆盖
|
||||
* @throws IORuntimeException IO异常
|
||||
* @see PathUtil#move(Path, Path, boolean)
|
||||
@ -2900,6 +2910,7 @@ public class FileUtil extends PathUtil {
|
||||
|
||||
/**
|
||||
* 将流的内容写入文件<br>
|
||||
* 此方法会自动关闭输入流
|
||||
*
|
||||
* @param dest 目标文件
|
||||
* @param in 输入流
|
||||
@ -2912,6 +2923,7 @@ public class FileUtil extends PathUtil {
|
||||
|
||||
/**
|
||||
* 将流的内容写入文件<br>
|
||||
* 此方法会自动关闭输入流
|
||||
*
|
||||
* @param in 输入流
|
||||
* @param fullFilePath 文件绝对路径
|
||||
@ -2923,7 +2935,7 @@ public class FileUtil extends PathUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件写入流中
|
||||
* 将文件写入流中,此方法不会概念比输出流
|
||||
*
|
||||
* @param file 文件
|
||||
* @param out 流
|
||||
|
@ -500,13 +500,25 @@ public class IoUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Reader中读取String,读取完毕后并不关闭Reader
|
||||
* 从Reader中读取String,读取完毕后关闭Reader
|
||||
*
|
||||
* @param reader Reader
|
||||
* @return String
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static String read(Reader reader) throws IORuntimeException {
|
||||
return read(reader, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从{@link Reader}中读取String
|
||||
*
|
||||
* @param reader {@link Reader}
|
||||
* @param isClose 是否关闭{@link Reader}
|
||||
* @return String
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static String read(Reader reader, boolean isClose) throws IORuntimeException {
|
||||
final StringBuilder builder = StrUtil.builder();
|
||||
final CharBuffer buffer = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
|
||||
try {
|
||||
@ -515,6 +527,10 @@ public class IoUtil {
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} finally{
|
||||
if(isClose){
|
||||
IoUtil.close(reader);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
@ -15,6 +15,19 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class FileNameUtil {
|
||||
|
||||
/**
|
||||
* .java文件扩展名
|
||||
*/
|
||||
public static final String EXT_JAVA = ".java";
|
||||
/**
|
||||
* .class文件扩展名
|
||||
*/
|
||||
public static final String EXT_CLASS = ".class";
|
||||
/**
|
||||
* .jar文件扩展名
|
||||
*/
|
||||
public static final String EXT_JAR = ".jar";
|
||||
|
||||
/**
|
||||
* 类Unix路径分隔符
|
||||
*/
|
||||
@ -232,5 +245,17 @@ public class FileNameUtil {
|
||||
public static boolean containsInvalid(String fileName) {
|
||||
return (false == StrUtil.isBlank(fileName)) && ReUtil.contains(FILE_NAME_INVALID_PATTERN_WIN, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件名检查文件类型,忽略大小写
|
||||
*
|
||||
* @param fileName 文件名,例如hutool.png
|
||||
* @param extNames 被检查的扩展名数组,同一文件类型可能有多种扩展名,扩展名不带“.”
|
||||
* @return 是否是指定扩展名的类型
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static boolean isType(String fileName, String... extNames) {
|
||||
return StrUtil.equalsAnyIgnoreCase(extName(fileName), extNames);
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------- name end
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public class FileReader extends FileWrapper {
|
||||
* 创建 FileReader
|
||||
* @param file 文件
|
||||
* @param charset 编码,使用 {@link CharsetUtil}
|
||||
* @return {@link FileReader}
|
||||
* @return FileReader
|
||||
*/
|
||||
public static FileReader create(File file, Charset charset){
|
||||
return new FileReader(file, charset);
|
||||
@ -40,7 +40,7 @@ public class FileReader extends FileWrapper {
|
||||
/**
|
||||
* 创建 FileReader, 编码:{@link FileWrapper#DEFAULT_CHARSET}
|
||||
* @param file 文件
|
||||
* @return {@link FileReader}
|
||||
* @return FileReader
|
||||
*/
|
||||
public static FileReader create(File file){
|
||||
return new FileReader(file);
|
||||
@ -244,19 +244,36 @@ public class FileReader extends FileWrapper {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将文件写入流中
|
||||
*
|
||||
* 将文件写入流中,此方法不会关闭比输出流
|
||||
*
|
||||
* @param out 流
|
||||
* @return 写出的流byte数
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public long writeToStream(OutputStream out) throws IORuntimeException {
|
||||
return writeToStream(out, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将文件写入流中
|
||||
*
|
||||
* @param out 流
|
||||
* @param isCloseOut 是否关闭输出流
|
||||
* @return 写出的流byte数
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public long writeToStream(OutputStream out, boolean isCloseOut) throws IORuntimeException {
|
||||
try (FileInputStream in = new FileInputStream(this.file)){
|
||||
return IoUtil.copy(in, out);
|
||||
}catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} finally{
|
||||
if(isCloseOut){
|
||||
IoUtil.close(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,12 @@
|
||||
package cn.hutool.core.io.file;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
@ -13,13 +20,6 @@ import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 文件写入器
|
||||
* @author Looly
|
||||
@ -32,7 +32,7 @@ public class FileWriter extends FileWrapper{
|
||||
* 创建 FileWriter
|
||||
* @param file 文件
|
||||
* @param charset 编码,使用 {@link CharsetUtil}
|
||||
* @return {@link FileWriter}
|
||||
* @return FileWriter
|
||||
*/
|
||||
public static FileWriter create(File file, Charset charset){
|
||||
return new FileWriter(file, charset);
|
||||
@ -41,7 +41,7 @@ public class FileWriter extends FileWrapper{
|
||||
/**
|
||||
* 创建 FileWriter, 编码:{@link FileWrapper#DEFAULT_CHARSET}
|
||||
* @param file 文件
|
||||
* @return {@link FileWriter}
|
||||
* @return FileWriter
|
||||
*/
|
||||
public static FileWriter create(File file){
|
||||
return new FileWriter(file);
|
||||
@ -302,13 +302,26 @@ public class FileWriter extends FileWrapper{
|
||||
|
||||
/**
|
||||
* 将流的内容写入文件<br>
|
||||
* 此方法不会关闭输入流
|
||||
*
|
||||
* 此方法会自动关闭输入流
|
||||
*
|
||||
* @param in 输入流,不关闭
|
||||
* @return dest
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public File writeFromStream(InputStream in) throws IORuntimeException {
|
||||
return writeFromStream(in, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将流的内容写入文件
|
||||
*
|
||||
* @param in 输入流,不关闭
|
||||
* @param isCloseIn 是否关闭输入流
|
||||
* @return dest
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public File writeFromStream(InputStream in, boolean isCloseIn) throws IORuntimeException {
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(FileUtil.touch(file));
|
||||
@ -317,6 +330,9 @@ public class FileWriter extends FileWrapper{
|
||||
throw new IORuntimeException(e);
|
||||
} finally {
|
||||
IoUtil.close(out);
|
||||
if(isCloseIn){
|
||||
IoUtil.close(in);
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
@ -91,6 +91,18 @@ public class PathUtil {
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历指定path下的文件并做处理
|
||||
*
|
||||
* @param start 起始路径,必须为目录
|
||||
* @param visitor {@link FileVisitor} 接口,用于自定义在访问文件时,访问目录前后等节点做的操作
|
||||
* @see Files#walkFileTree(Path, java.util.Set, int, FileVisitor)
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static void walkFiles(Path start, FileVisitor<? super Path> visitor) {
|
||||
walkFiles(start, -1, visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历指定path下的文件并做处理
|
||||
*
|
||||
|
@ -3,11 +3,9 @@ package cn.hutool.core.io.resource;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
@ -59,11 +57,6 @@ public class BytesResource implements Resource, Serializable {
|
||||
return new ByteArrayInputStream(this.bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader(Charset charset) {
|
||||
return new BufferedReader(new StringReader(readStr(charset)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readStr(Charset charset) throws IORuntimeException {
|
||||
return StrUtil.str(this.bytes, charset);
|
||||
|
@ -0,0 +1,90 @@
|
||||
package cn.hutool.core.io.resource;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* {@link CharSequence}资源,字符串做为资源
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class CharSequenceResource implements Resource, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final CharSequence data;
|
||||
private final CharSequence name;
|
||||
private final Charset charset;
|
||||
|
||||
/**
|
||||
* 构造,使用UTF8编码
|
||||
*
|
||||
* @param data 资源数据
|
||||
*/
|
||||
public CharSequenceResource(CharSequence data) {
|
||||
this(data, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造,使用UTF8编码
|
||||
*
|
||||
* @param data 资源数据
|
||||
* @param name 资源名称
|
||||
*/
|
||||
public CharSequenceResource(CharSequence data, String name) {
|
||||
this(data, name, CharsetUtil.CHARSET_UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param data 资源数据
|
||||
* @param name 资源名称
|
||||
* @param charset 编码
|
||||
*/
|
||||
public CharSequenceResource(CharSequence data, CharSequence name, Charset charset) {
|
||||
this.data = data;
|
||||
this.name = name;
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStream() {
|
||||
return new ByteArrayInputStream(readBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader(Charset charset) {
|
||||
return IoUtil.getReader(new StringReader(this.data.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readStr(Charset charset) throws IORuntimeException {
|
||||
return this.data.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readBytes() throws IORuntimeException {
|
||||
return this.data.toString().getBytes(this.charset);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package cn.hutool.core.io.resource;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
|
||||
import javax.tools.FileObject;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* {@link FileObject} 资源包装
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class FileObjectResource implements Resource {
|
||||
|
||||
private final FileObject fileObject;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param fileObject {@link FileObject}
|
||||
*/
|
||||
public FileObjectResource(FileObject fileObject) {
|
||||
this.fileObject = fileObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始的{@link FileObject}
|
||||
*
|
||||
* @return {@link FileObject}
|
||||
*/
|
||||
public FileObject getFileObject() {
|
||||
return this.fileObject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.fileObject.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
try {
|
||||
return this.fileObject.toUri().toURL();
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStream() {
|
||||
try {
|
||||
return this.fileObject.openInputStream();
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader(Charset charset) {
|
||||
try {
|
||||
return IoUtil.getReader(this.fileObject.openReader(false));
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,8 @@
|
||||
package cn.hutool.core.io.resource;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 基于{@link InputStream}的资源获取器<br>
|
||||
@ -56,32 +51,4 @@ public class InputStreamResource implements Resource, Serializable {
|
||||
public InputStream getStream() {
|
||||
return this.in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader(Charset charset) {
|
||||
return IoUtil.getReader(this.in, charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readStr(Charset charset) throws IORuntimeException {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = getReader(charset);
|
||||
return IoUtil.read(reader);
|
||||
} finally {
|
||||
IoUtil.close(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readBytes() throws IORuntimeException {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getStream();
|
||||
return IoUtil.readBytes(in);
|
||||
} finally {
|
||||
IoUtil.close(in);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -58,7 +58,9 @@ public interface Resource {
|
||||
* @param charset 编码
|
||||
* @return {@link BufferedReader}
|
||||
*/
|
||||
BufferedReader getReader(Charset charset);
|
||||
default BufferedReader getReader(Charset charset){
|
||||
return IoUtil.getReader(getStream(), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取资源内容,读取完毕后会关闭流<br>
|
||||
@ -68,7 +70,9 @@ public interface Resource {
|
||||
* @return 读取资源内容
|
||||
* @throws IORuntimeException 包装{@link IOException}
|
||||
*/
|
||||
String readStr(Charset charset) throws IORuntimeException;
|
||||
default String readStr(Charset charset) throws IORuntimeException{
|
||||
return IoUtil.read(getReader(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取资源内容,读取完毕后会关闭流<br>
|
||||
@ -88,5 +92,7 @@ public interface Resource {
|
||||
* @return 读取资源内容
|
||||
* @throws IORuntimeException 包装IOException
|
||||
*/
|
||||
byte[] readBytes() throws IORuntimeException;
|
||||
default byte[] readBytes() throws IORuntimeException{
|
||||
return IoUtil.readBytes(getStream());
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,7 @@
|
||||
package cn.hutool.core.io.resource;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
@ -17,13 +9,11 @@ import java.nio.charset.Charset;
|
||||
*
|
||||
* @author looly
|
||||
* @since 4.1.0
|
||||
* @see CharSequenceResource
|
||||
*/
|
||||
public class StringResource implements Resource, Serializable {
|
||||
public class StringResource extends CharSequenceResource {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String data;
|
||||
private final String name;
|
||||
private final Charset charset;
|
||||
|
||||
/**
|
||||
* 构造,使用UTF8编码
|
||||
@ -31,7 +21,7 @@ public class StringResource implements Resource, Serializable {
|
||||
* @param data 资源数据
|
||||
*/
|
||||
public StringResource(String data) {
|
||||
this(data, null);
|
||||
super(data, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,7 +31,7 @@ public class StringResource implements Resource, Serializable {
|
||||
* @param name 资源名称
|
||||
*/
|
||||
public StringResource(String data, String name) {
|
||||
this(data, name, CharsetUtil.CHARSET_UTF_8);
|
||||
super(data, name, CharsetUtil.CHARSET_UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,39 +42,6 @@ public class StringResource implements Resource, Serializable {
|
||||
* @param charset 编码
|
||||
*/
|
||||
public StringResource(String data, String name, Charset charset) {
|
||||
this.data = data;
|
||||
this.name = name;
|
||||
this.charset = charset;
|
||||
super(data, name, charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStream() {
|
||||
return new ByteArrayInputStream(readBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader(Charset charset) {
|
||||
return IoUtil.getReader(new StringReader(this.data));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String readStr(Charset charset) throws IORuntimeException {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readBytes() throws IORuntimeException {
|
||||
return this.data.getBytes(this.charset);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,17 +1,13 @@
|
||||
package cn.hutool.core.io.resource;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* URL资源访问类
|
||||
@ -72,40 +68,6 @@ public class UrlResource implements Resource, Serializable{
|
||||
return URLUtil.getStream(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得Reader
|
||||
* @param charset 编码
|
||||
* @return {@link BufferedReader}
|
||||
* @since 3.0.1
|
||||
*/
|
||||
@Override
|
||||
public BufferedReader getReader(Charset charset){
|
||||
return URLUtil.getReader(this.url, charset);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------- read
|
||||
@Override
|
||||
public String readStr(Charset charset) throws IORuntimeException{
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = getReader(charset);
|
||||
return IoUtil.read(reader);
|
||||
} finally {
|
||||
IoUtil.close(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readBytes() throws IORuntimeException{
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = getStream();
|
||||
return IoUtil.readBytes(in);
|
||||
} finally {
|
||||
IoUtil.close(in);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得File
|
||||
* @return {@link File}
|
||||
|
@ -59,7 +59,7 @@ public class JarClassLoader extends URLClassLoader {
|
||||
method.setAccessible(true);
|
||||
final List<File> jars = loopJar(jarFile);
|
||||
for (File jar : jars) {
|
||||
ReflectUtil.invoke(loader, method, new Object[]{jar.toURI().toURL()});
|
||||
ReflectUtil.invoke(loader, method, jar.toURI().toURL());
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
@ -0,0 +1,52 @@
|
||||
package cn.hutool.core.lang;
|
||||
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.util.ClassLoaderUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.security.SecureClassLoader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 资源类加载器,可以加载任意类型的资源类
|
||||
*
|
||||
* @param <T> {@link Resource}接口实现类
|
||||
* @author looly
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public class ResourceClassLoader<T extends Resource> extends SecureClassLoader {
|
||||
|
||||
private final Map<String, T> resourceMap;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param parentClassLoader 父类加载器,null表示默认当前上下文加载器
|
||||
* @param resourceMap 资源map
|
||||
*/
|
||||
public ResourceClassLoader(ClassLoader parentClassLoader, Map<String, T> resourceMap) {
|
||||
super(ObjectUtil.defaultIfNull(parentClassLoader, ClassLoaderUtil.getClassLoader()));
|
||||
this.resourceMap = ObjectUtil.defaultIfNull(resourceMap, new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加需要加载的类资源
|
||||
* @param resource 资源,可以是文件、流或者字符串
|
||||
* @return this
|
||||
*/
|
||||
public ResourceClassLoader<T> addResource(T resource){
|
||||
this.resourceMap.put(resource.getName(), resource);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
final Resource resource = resourceMap.get(name);
|
||||
if (null != resource) {
|
||||
final byte[] bytes = resource.readBytes();
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
}
|
||||
return super.findClass(name);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import cn.hutool.core.io.FastByteArrayOutputStream;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -19,6 +20,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
@ -44,6 +46,37 @@ public class ZipUtil {
|
||||
*/
|
||||
private static final Charset DEFAULT_CHARSET = CharsetUtil.defaultCharset();
|
||||
|
||||
/**
|
||||
* 将Zip文件转换为{@link ZipFile}
|
||||
*
|
||||
* @param file zip文件
|
||||
* @param charset 解析zip文件的编码,null表示{@link CharsetUtil#CHARSET_UTF_8}
|
||||
* @return {@link ZipFile}
|
||||
*/
|
||||
public static ZipFile toZipFile(File file, Charset charset) {
|
||||
try {
|
||||
return new ZipFile(file, ObjectUtil.defaultIfNull(charset, CharsetUtil.CHARSET_UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定{@link ZipEntry}的流,用于读取这个entry的内容
|
||||
*
|
||||
* @param zipFile {@link ZipFile}
|
||||
* @param zipEntry {@link ZipEntry}
|
||||
* @return 流
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static InputStream getStream(ZipFile zipFile, ZipEntry zipEntry) {
|
||||
try {
|
||||
return zipFile.getInputStream(zipEntry);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包到当前目录,使用默认编码UTF-8
|
||||
*
|
||||
@ -190,7 +223,7 @@ public class ZipUtil {
|
||||
/**
|
||||
* 对文件或文件目录进行压缩
|
||||
*
|
||||
* @param out 生成的Zip到的目标流,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
|
||||
* @param out 生成的Zip到的目标流,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
|
||||
* @param charset 编码
|
||||
* @param withSrcDir 是否包含被打包目录,只针对压缩目录有效。若为false,则只压缩目录下的文件或目录,为true则将本目录也压缩
|
||||
* @param filter 文件过滤器,通过实现此接口,自定义要过滤的文件(过滤掉哪些文件或文件夹不加入压缩)
|
||||
@ -205,16 +238,16 @@ public class ZipUtil {
|
||||
/**
|
||||
* 对文件或文件目录进行压缩
|
||||
*
|
||||
* @param zipOutputStream 生成的Zip到的目标流,不关闭此流
|
||||
* @param withSrcDir 是否包含被打包目录,只针对压缩目录有效。若为false,则只压缩目录下的文件或目录,为true则将本目录也压缩
|
||||
* @param filter 文件过滤器,通过实现此接口,自定义要过滤的文件(过滤掉哪些文件或文件夹不加入压缩)
|
||||
* @param srcFiles 要压缩的源文件或目录。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径
|
||||
* @param zipOutputStream 生成的Zip到的目标流,不关闭此流
|
||||
* @param withSrcDir 是否包含被打包目录,只针对压缩目录有效。若为false,则只压缩目录下的文件或目录,为true则将本目录也压缩
|
||||
* @param filter 文件过滤器,通过实现此接口,自定义要过滤的文件(过滤掉哪些文件或文件夹不加入压缩)
|
||||
* @param srcFiles 要压缩的源文件或目录。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.1.1
|
||||
*/
|
||||
public static void zip(ZipOutputStream zipOutputStream, boolean withSrcDir, FileFilter filter, File... srcFiles) throws IORuntimeException {
|
||||
String srcRootDir;
|
||||
try{
|
||||
try {
|
||||
for (File srcFile : srcFiles) {
|
||||
if (null == srcFile) {
|
||||
continue;
|
||||
@ -300,7 +333,7 @@ public class ZipUtil {
|
||||
*
|
||||
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
|
||||
* @param paths 流数据在压缩文件中的路径或文件名
|
||||
* @param ins 要压缩的源
|
||||
* @param ins 要压缩的源,添加完成后自动关闭流
|
||||
* @return 压缩文件
|
||||
* @throws UtilException IO异常
|
||||
* @since 3.0.9
|
||||
@ -333,7 +366,31 @@ public class ZipUtil {
|
||||
try {
|
||||
out = getZipOutputStream(zipFile, charset);
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
addFile(ins[i], paths[i], out);
|
||||
add(ins[i], paths[i], out);
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(out);
|
||||
}
|
||||
return zipFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对流中的数据加入到压缩文件<br>
|
||||
* 路径列表和流列表长度必须一致
|
||||
*
|
||||
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
|
||||
* @param charset 编码
|
||||
* @param resources 需要压缩的资源,资源的路径为{@link Resource#getName()}
|
||||
* @return 压缩文件
|
||||
* @throws UtilException IO异常
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static File zip(File zipFile, Charset charset, Resource... resources) throws UtilException {
|
||||
ZipOutputStream out = null;
|
||||
try {
|
||||
out = getZipOutputStream(zipFile, charset);
|
||||
for (Resource resource : resources) {
|
||||
add(resource.getStream(), resource.getName(), out);
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(out);
|
||||
@ -437,17 +494,10 @@ public class ZipUtil {
|
||||
* @param outFile 解压到的目录
|
||||
* @param charset 编码
|
||||
* @return 解压的目录
|
||||
* @throws UtilException IO异常
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public static File unzip(File zipFile, File outFile, Charset charset) throws UtilException {
|
||||
ZipFile zip;
|
||||
try {
|
||||
zip = new ZipFile(zipFile, charset);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return unzip(zip, outFile);
|
||||
public static File unzip(File zipFile, File outFile, Charset charset) {
|
||||
return unzip(toZipFile(zipFile, charset), outFile);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -460,66 +510,71 @@ public class ZipUtil {
|
||||
* @since 4.5.8
|
||||
*/
|
||||
public static File unzip(ZipFile zipFile, File outFile) throws IORuntimeException {
|
||||
if(outFile.exists() && outFile.isFile()){
|
||||
if (outFile.exists() && outFile.isFile()) {
|
||||
throw new UtilException("Target path [{}] exist!", outFile.getAbsolutePath());
|
||||
}
|
||||
try {
|
||||
final Enumeration<? extends ZipEntry> em = zipFile.entries();
|
||||
ZipEntry zipEntry;
|
||||
File outItemFile;
|
||||
while (em.hasMoreElements()) {
|
||||
zipEntry = em.nextElement();
|
||||
// FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/
|
||||
outItemFile = FileUtil.file(outFile, zipEntry.getName());
|
||||
if (zipEntry.isDirectory()) {
|
||||
// 创建对应目录
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
outItemFile.mkdirs();
|
||||
} else {
|
||||
// 写出文件
|
||||
write(zipFile, zipEntry, outItemFile);
|
||||
}
|
||||
read(zipFile, (zipEntry) -> {
|
||||
// FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/
|
||||
File outItemFile = FileUtil.file(outFile, zipEntry.getName());
|
||||
if (zipEntry.isDirectory()) {
|
||||
// 创建对应目录
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
outItemFile.mkdirs();
|
||||
} else {
|
||||
// 写出文件
|
||||
write(zipFile, zipEntry, outItemFile);
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(zipFile);
|
||||
}
|
||||
});
|
||||
|
||||
return outFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取压缩包中的指定文件流
|
||||
*
|
||||
* @param zipFile 压缩文件
|
||||
* @param path 需要提取文件的文件名或路径
|
||||
* @param path 需要提取文件的文件名或路径
|
||||
* @return 压缩文件流,如果未找到返回{@code null}
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static InputStream get(File zipFile, Charset charset, String path){
|
||||
try {
|
||||
return get(new ZipFile(zipFile, charset), path);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
public static InputStream get(File zipFile, Charset charset, String path) {
|
||||
return get(toZipFile(zipFile, charset), path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取压缩包中的指定文件流
|
||||
*
|
||||
* @param zipFile 压缩文件
|
||||
* @param path 需要提取文件的文件名或路径
|
||||
* @param path 需要提取文件的文件名或路径
|
||||
* @return 压缩文件流,如果未找到返回{@code null}
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static InputStream get(ZipFile zipFile, String path){
|
||||
public static InputStream get(ZipFile zipFile, String path) {
|
||||
final ZipEntry entry = zipFile.getEntry(path);
|
||||
if(null != entry){
|
||||
try {
|
||||
return zipFile.getInputStream(entry);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
if (null != entry) {
|
||||
return getStream(zipFile, entry);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取并处理Zip文件中的每一个{@link ZipEntry}
|
||||
*
|
||||
* @param zipFile Zip文件
|
||||
* @param consumer {@link ZipEntry}处理器
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static void read(ZipFile zipFile, Consumer<ZipEntry> consumer) {
|
||||
try {
|
||||
final Enumeration<? extends ZipEntry> em = zipFile.entries();
|
||||
while (em.hasMoreElements()) {
|
||||
consumer.accept(em.nextElement());
|
||||
}
|
||||
} finally {
|
||||
IoUtil.close(zipFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解压<br>
|
||||
* ZIP条目不使用高速缓冲。
|
||||
@ -549,27 +604,40 @@ public class ZipUtil {
|
||||
* @since 4.5.8
|
||||
*/
|
||||
public static File unzip(ZipInputStream zipStream, File outFile) throws UtilException {
|
||||
read(zipStream, (zipEntry) -> {
|
||||
// FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/
|
||||
File outItemFile = FileUtil.file(outFile, zipEntry.getName());
|
||||
if (zipEntry.isDirectory()) {
|
||||
// 目录
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
outItemFile.mkdirs();
|
||||
} else {
|
||||
// 文件
|
||||
FileUtil.writeFromStream(zipStream, outItemFile);
|
||||
}
|
||||
});
|
||||
return outFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取并处理Zip流中的每一个{@link ZipEntry}
|
||||
*
|
||||
* @param zipStream zip文件流,包含编码信息
|
||||
* @param consumer {@link ZipEntry}处理器
|
||||
* @since 5.5.2
|
||||
*/
|
||||
public static void read(ZipInputStream zipStream, Consumer<ZipEntry> consumer) {
|
||||
try {
|
||||
ZipEntry zipEntry;
|
||||
File outItemFile;
|
||||
while (null != (zipEntry = zipStream.getNextEntry())) {
|
||||
// FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/
|
||||
outItemFile = FileUtil.file(outFile, zipEntry.getName());
|
||||
if (zipEntry.isDirectory()) {
|
||||
// 目录
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
outItemFile.mkdirs();
|
||||
} else {
|
||||
// 文件
|
||||
FileUtil.writeFromStream(zipStream, outItemFile);
|
||||
}
|
||||
consumer.accept(zipEntry);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UtilException(e);
|
||||
} finally {
|
||||
IoUtil.close(zipStream);
|
||||
}
|
||||
return outFile;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -622,17 +690,15 @@ public class ZipUtil {
|
||||
public static byte[] unzipFileBytes(File zipFile, Charset charset, String name) {
|
||||
ZipFile zipFileObj = null;
|
||||
try {
|
||||
zipFileObj = new ZipFile(zipFile, charset);
|
||||
zipFileObj = toZipFile(zipFile, charset);
|
||||
final Enumeration<ZipEntry> em = (Enumeration<ZipEntry>) zipFileObj.entries();
|
||||
ZipEntry zipEntry;
|
||||
while (em.hasMoreElements()) {
|
||||
zipEntry = em.nextElement();
|
||||
if ((false == zipEntry.isDirectory()) && name.equals(zipEntry.getName())) {
|
||||
return IoUtil.readBytes(zipFileObj.getInputStream(zipEntry));
|
||||
return IoUtil.readBytes(getStream(zipFileObj, zipEntry));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UtilException(e);
|
||||
} finally {
|
||||
IoUtil.close(zipFileObj);
|
||||
}
|
||||
@ -946,8 +1012,8 @@ public class ZipUtil {
|
||||
* @return {@link ZipOutputStream}
|
||||
*/
|
||||
private static ZipOutputStream getZipOutputStream(OutputStream out, Charset charset) {
|
||||
if(out instanceof ZipOutputStream) {
|
||||
return (ZipOutputStream)out;
|
||||
if (out instanceof ZipOutputStream) {
|
||||
return (ZipOutputStream) out;
|
||||
}
|
||||
return new ZipOutputStream(out, ObjectUtil.defaultIfNull(charset, DEFAULT_CHARSET));
|
||||
}
|
||||
@ -980,7 +1046,7 @@ public class ZipUtil {
|
||||
zip(childFile, srcRootDir, out, filter);
|
||||
}
|
||||
} else {// 如果是文件或其它符号,则直接压缩该文件
|
||||
addFile(file, subPath, out);
|
||||
add(file, subPath, out);
|
||||
}
|
||||
}
|
||||
|
||||
@ -993,19 +1059,19 @@ public class ZipUtil {
|
||||
* @throws UtilException IO异常
|
||||
* @since 4.0.5
|
||||
*/
|
||||
private static void addFile(File file, String path, ZipOutputStream out) throws UtilException {
|
||||
addFile(FileUtil.getInputStream(file), path, out);
|
||||
private static void add(File file, String path, ZipOutputStream out) throws UtilException {
|
||||
add(FileUtil.getInputStream(file), path, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加文件流到压缩包,添加后关闭流
|
||||
*
|
||||
* @param in 需要压缩的输入流
|
||||
* @param in 需要压缩的输入流,使用完后自动关闭
|
||||
* @param path 压缩的路径
|
||||
* @param out 压缩文件存储对象
|
||||
* @throws UtilException IO异常
|
||||
*/
|
||||
private static void addFile(InputStream in, String path, ZipOutputStream out) throws UtilException {
|
||||
private static void add(InputStream in, String path, ZipOutputStream out) throws UtilException {
|
||||
if (null == in) {
|
||||
return;
|
||||
}
|
||||
@ -1058,7 +1124,7 @@ public class ZipUtil {
|
||||
}
|
||||
|
||||
// 压缩文件不能位于被压缩的目录内
|
||||
if(srcFile.isDirectory() && FileUtil.isSub(srcFile, zipFile.getParentFile())){
|
||||
if (srcFile.isDirectory() && FileUtil.isSub(srcFile, zipFile.getParentFile())) {
|
||||
throw new UtilException("Zip file path [{}] must not be the child directory of [{}] !", zipFile.getPath(), srcFile.getPath());
|
||||
}
|
||||
}
|
||||
@ -1086,15 +1152,7 @@ public class ZipUtil {
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
private static void write(ZipFile zipFile, ZipEntry zipEntry, File outItemFile) throws IORuntimeException {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = zipFile.getInputStream(zipEntry);
|
||||
FileUtil.writeFromStream(in, outItemFile);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
} finally {
|
||||
IoUtil.close(in);
|
||||
}
|
||||
FileUtil.writeFromStream(getStream(zipFile, zipEntry), outItemFile);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,7 @@ public class JavaSourceCompilerTest {
|
||||
*/
|
||||
@Test
|
||||
public void testCompile() throws ClassNotFoundException {
|
||||
// 依赖A,编译B和C
|
||||
final File libFile = ZipUtil.zip(FileUtil.file("lib.jar"),
|
||||
new String[]{"a/A.class", "a/A$1.class", "a/A$InnerClass.class"},
|
||||
new InputStream[]{
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.hutool.core.io.resource;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -10,4 +11,12 @@ public class ResourceUtilTest {
|
||||
final String str = ResourceUtil.readUtf8Str("test.xml");
|
||||
Assert.assertNotNull(str);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringResourceTest(){
|
||||
final StringResource stringResource = new StringResource("testData", "test");
|
||||
Assert.assertEquals("test", stringResource.getName());
|
||||
Assert.assertArrayEquals("testData".getBytes(), stringResource.readBytes());
|
||||
Assert.assertArrayEquals("testData".getBytes(), IoUtil.readBytes(stringResource.getStream()));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user