diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipUtil.java index 7f3fffbd7..d7608b1a3 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/compress/ZipUtil.java @@ -23,9 +23,11 @@ import org.dromara.hutool.core.io.file.PathUtil; import org.dromara.hutool.core.io.resource.Resource; import org.dromara.hutool.core.io.stream.FastByteArrayOutputStream; import org.dromara.hutool.core.io.stream.LimitedInputStream; +import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.net.url.URLUtil; +import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.ByteUtil; -import org.dromara.hutool.core.text.CharUtil; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.ObjUtil; @@ -37,6 +39,7 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.function.Consumer; +import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; @@ -985,6 +988,32 @@ public class ZipUtil { return fileNames; } + /** + * 获取对应URL路径的jar文件,支持包括file://xxx这类路径
+ * 来自:org.springframework.core.io.support.PathMatchingResourcePatternResolver#getJarFile + * + * @param jarFileUrl jar文件路径 + * @return {@link JarFile} + * @throws IORuntimeException IO异常 + * @since 6.0.0 + */ + public static JarFile ofJar(String jarFileUrl) throws IORuntimeException{ + Assert.notBlank(jarFileUrl, "Jar file url is blank!"); + + if(jarFileUrl.startsWith(URLUtil.FILE_URL_PREFIX)){ + try{ + jarFileUrl = URLUtil.toURI(jarFileUrl).getSchemeSpecificPart(); + } catch (final HutoolException e){ + jarFileUrl = jarFileUrl.substring(URLUtil.FILE_URL_PREFIX.length()); + } + } + try { + return new JarFile(jarFileUrl); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + } + // ---------------------------------------------------------------------------------------------- Private method start /** diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/JarResource.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/JarResource.java new file mode 100644 index 000000000..fab3f4a43 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/JarResource.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.io.resource; + +import org.dromara.hutool.core.compress.ZipUtil; +import org.dromara.hutool.core.io.IORuntimeException; +import org.dromara.hutool.core.net.url.URLUtil; + +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.util.jar.JarFile; + +/** + * Jar包资源对象 + * + * @author looly + */ +public class JarResource extends UrlResource { + private static final long serialVersionUID = 1L; + + /** + * 构造 + * + * @param uri JAR的URI + */ + public JarResource(final URI uri) { + super(uri); + } + + /** + * 构造 + * + * @param url JAR的URL + */ + public JarResource(final URL url) { + super(url); + } + + /** + * 构造 + * + * @param url JAR的URL + * @param name 资源名称 + */ + public JarResource(final URL url, final String name) { + super(url, name); + } + + /** + * 获取URL对应的{@link JarFile}对象 + * + * @return {@link JarFile} + * @throws IORuntimeException IO异常 + */ + public JarFile getJarFile() throws IORuntimeException { + try { + return doGetJarFile(); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + } + + /** + * 获取{@link JarFile}
+ * 首席按通过openConnection方式获取,如果得到的不是{@link JarURLConnection},
+ * 则尝试去除WAR、JAR等协议分隔符,裁剪分隔符前段来直接获取{@link JarFile}。 + * + * @return {@link JarFile} + * @throws IOException IO异常 + */ + private JarFile doGetJarFile() throws IOException { + final URLConnection con = getUrl().openConnection(); + if (con instanceof JarURLConnection) { + final JarURLConnection jarCon = (JarURLConnection) con; + return jarCon.getJarFile(); + } else { + final String urlFile = getUrl().getFile(); + int separatorIndex = urlFile.indexOf(URLUtil.WAR_URL_SEPARATOR); + if (separatorIndex == -1) { + separatorIndex = urlFile.indexOf(URLUtil.JAR_URL_SEPARATOR); + } + if (separatorIndex != -1) { + return ZipUtil.ofJar(urlFile.substring(0, separatorIndex)); + } else { + return new JarFile(urlFile); + } + } + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/MultiResource.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/MultiResource.java index 93b05128b..bf4d2a909 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/MultiResource.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/MultiResource.java @@ -53,9 +53,9 @@ public class MultiResource implements Resource, Iterable, Iterator resources) { - if(resources instanceof List) { - this.resources = (List)resources; - }else { + if (resources instanceof List) { + this.resources = (List) resources; + } else { this.resources = ListUtil.of(resources); } } @@ -138,6 +138,7 @@ public class MultiResource implements Resource, Iterable, Iterator, Iterator iterable) { + iterable.forEach((this::add)); + return this; + } + } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/ResourceFinder.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/ResourceFinder.java new file mode 100644 index 000000000..7e95a9040 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/ResourceFinder.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2023. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.io.resource; + +import org.dromara.hutool.core.collection.iter.EnumerationIter; +import org.dromara.hutool.core.compress.ZipUtil; +import org.dromara.hutool.core.io.IORuntimeException; +import org.dromara.hutool.core.io.IoUtil; +import org.dromara.hutool.core.net.url.URLUtil; +import org.dromara.hutool.core.text.AntPathMatcher; +import org.dromara.hutool.core.text.CharUtil; +import org.dromara.hutool.core.text.StrUtil; + +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.ZipException; + +public class ResourceFinder { + + private final ClassLoader classLoader; + private final AntPathMatcher pathMatcher; + + /** + * 构造 + * + * @param classLoader 类加载器,用于定义查找资源的范围 + */ + public ResourceFinder(final ClassLoader classLoader) { + this.classLoader = classLoader; + this.pathMatcher = new AntPathMatcher(); + } + + /** + * 查找给定表达式对应的资源 + * + * @param locationPattern 路径表达式 + * @return {@link MultiResource} + */ + public MultiResource find(final String locationPattern) { + // 根目录 + final String rootDirPath = determineRootDir(locationPattern); + // 子表达式 + final String subPattern = locationPattern.substring(rootDirPath.length()); + + final MultiResource result = new MultiResource(); + // 遍历根目录下所有资源,并过滤保留符合条件的资源 + for (final Resource rootResource : ResourceUtil.getResources(rootDirPath, classLoader)) { + if (URLUtil.isJarURL(rootResource.getUrl())) { + try { + result.addAll(findInJar(rootResource, subPattern)); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + } else { + result.addAll(findInDir(rootResource, subPattern)); + } + } + + return result; + } + + /** + * 查找jar包中的资源 + * + * @param rootResource 根资源,为jar包文件 + * @param subPattern 子表达式,如 *.xml + * @return 符合条件的资源 + * @throws IOException IO异常 + */ + protected MultiResource findInJar(final Resource rootResource, final String subPattern) throws IOException { + final URL rootDirURL = rootResource.getUrl(); + final URLConnection conn = rootDirURL.openConnection(); + + final JarFile jarFile; + final String jarFileUrl; + String rootEntryPath; + final boolean closeJarFile; + + if (conn instanceof JarURLConnection) { + final JarURLConnection jarCon = (JarURLConnection) conn; + URLUtil.useCachesIfNecessary(jarCon); + jarFile = jarCon.getJarFile(); + final JarEntry jarEntry = jarCon.getJarEntry(); + rootEntryPath = (jarEntry != null ? jarEntry.getName() : StrUtil.EMPTY); + closeJarFile = !jarCon.getUseCaches(); + } else { + // + final String urlFile = rootDirURL.getFile(); + try { + int separatorIndex = urlFile.indexOf(URLUtil.WAR_URL_SEPARATOR); + if (separatorIndex == -1) { + separatorIndex = urlFile.indexOf(URLUtil.JAR_URL_SEPARATOR); + } + if (separatorIndex != -1) { + jarFileUrl = urlFile.substring(0, separatorIndex); + rootEntryPath = urlFile.substring(separatorIndex + 2); // both separators are 2 chars + jarFile = ZipUtil.ofJar(jarFileUrl); + } else { + jarFile = new JarFile(urlFile); + rootEntryPath = StrUtil.EMPTY; + } + closeJarFile = true; + } catch (final ZipException ex) { + return new MultiResource(); + } + } + + rootEntryPath = StrUtil.addSuffixIfNot(rootEntryPath, StrUtil.SLASH); + // 遍历jar中的entry,筛选之 + final MultiResource result = new MultiResource(); + + try { + String entryPath; + for (final JarEntry entry : new EnumerationIter<>(jarFile.entries())) { + entryPath = entry.getName(); + if (entryPath.startsWith(rootEntryPath)) { + final String relativePath = entryPath.substring(rootEntryPath.length()); + if (pathMatcher.match(subPattern, relativePath)) { + result.add(new UrlResource(URLUtil.getURL(rootDirURL, relativePath))); + } + } + } + } finally { + if (closeJarFile) { + IoUtil.closeQuietly(jarFile); + } + } + + return result; + } + + protected MultiResource findInDir(final Resource rootResource, final String subPattern) { + + } + + /** + * 根据给定的路径表达式,找到跟路径
+ * 根路径即不包含表达式的路径,如 "/WEB-INF/*.xml" 返回 "/WEB-INF/" + * + * @param location 路径表达式 + */ + protected String determineRootDir(final String location) { + final int prefixEnd = location.indexOf(':') + 1; + int rootDirEnd = location.length(); + while (rootDirEnd > prefixEnd && pathMatcher.isPattern(location.substring(prefixEnd, rootDirEnd))) { + rootDirEnd = location.lastIndexOf(CharUtil.SLASH, rootDirEnd - 2) + 1; + } + if (rootDirEnd == 0) { + rootDirEnd = prefixEnd; + } + return location.substring(0, rootDirEnd); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/UrlResource.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/UrlResource.java index 5ad33431a..490aeffed 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/UrlResource.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/UrlResource.java @@ -12,22 +12,23 @@ package org.dromara.hutool.core.io.resource; -import org.dromara.hutool.core.io.IORuntimeException; -import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.io.file.FileNameUtil; -import org.dromara.hutool.core.net.NetUtil; -import org.dromara.hutool.core.util.ObjUtil; +import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.net.url.URLUtil; +import org.dromara.hutool.core.util.ObjUtil; -import java.io.*; -import java.net.*; +import java.io.File; +import java.io.InputStream; +import java.io.Serializable; +import java.net.URI; +import java.net.URL; /** * URL资源访问类 - * @author Looly * + * @author Looly */ -public class UrlResource implements Resource, Serializable{ +public class UrlResource implements Resource, Serializable { private static final long serialVersionUID = 1L; protected URL url; @@ -35,8 +36,10 @@ public class UrlResource implements Resource, Serializable{ protected String name; //-------------------------------------------------------------------------------------- Constructor start + /** * 构造 + * * @param uri URI * @since 5.7.21 */ @@ -46,6 +49,7 @@ public class UrlResource implements Resource, Serializable{ /** * 构造 + * * @param url URL */ public UrlResource(final URL url) { @@ -54,12 +58,13 @@ public class UrlResource implements Resource, Serializable{ /** * 构造 - * @param url URL,允许为空 + * + * @param url URL,允许为空 * @param name 资源名称 */ public UrlResource(final URL url, final String name) { this.url = url; - if(null != url && URLUtil.URL_PROTOCOL_FILE.equals(url.getProtocol())){ + if (null != url && URLUtil.URL_PROTOCOL_FILE.equals(url.getProtocol())) { this.lastModified = FileUtil.file(url).lastModified(); } this.name = ObjUtil.defaultIfNull(name, () -> (null != url ? FileNameUtil.getName(url.getPath()) : null)); @@ -73,7 +78,7 @@ public class UrlResource implements Resource, Serializable{ } @Override - public URL getUrl(){ + public URL getUrl() { return this.url; } @@ -83,8 +88,8 @@ public class UrlResource implements Resource, Serializable{ } @Override - public InputStream getStream() throws NoResourceException{ - if(null == this.url){ + public InputStream getStream() throws NoResourceException { + if (null == this.url) { throw new NoResourceException("Resource URL is null!"); } return URLUtil.getStream(url); @@ -98,18 +103,31 @@ public class UrlResource implements Resource, Serializable{ /** * 获得File + * * @return {@link File} */ - public File getFile(){ + public File getFile() { return FileUtil.file(this.url); } /** * 返回路径 + * * @return 返回URL路径 */ @Override public String toString() { return (null == this.url) ? "null" : this.url.toString(); } + + /** + * 获取相对于本资源的资源 + * + * @param relativePath 相对路径 + * @return 子资源 + * @since 6.0.0 + */ + public UrlResource createRelative(final String relativePath) { + return new UrlResource(URLUtil.getURL(getUrl(), relativePath)); + } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/VfsResource.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/VfsResource.java index cead0f4a4..248cc4b0d 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/VfsResource.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/resource/VfsResource.java @@ -16,6 +16,7 @@ import org.dromara.hutool.core.classloader.ClassLoaderUtil; import org.dromara.hutool.core.lang.Assert; import org.dromara.hutool.core.reflect.method.MethodUtil; +import java.io.File; import java.io.InputStream; import java.lang.reflect.Method; import java.net.URL; @@ -37,6 +38,7 @@ public class VfsResource implements Resource { private static final Method VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED; private static final Method VIRTUAL_FILE_METHOD_TO_URL; private static final Method VIRTUAL_FILE_METHOD_GET_NAME; + private static final Method VIRTUAL_FILE_METHOD_GET_PHYSICAL_FILE; static { final Class virtualFile = ClassLoaderUtil.loadClass(VFS3_PKG + "VirtualFile"); @@ -47,6 +49,7 @@ public class VfsResource implements Resource { VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED = virtualFile.getMethod("getLastModified"); VIRTUAL_FILE_METHOD_TO_URL = virtualFile.getMethod("toURL"); VIRTUAL_FILE_METHOD_GET_NAME = virtualFile.getMethod("getName"); + VIRTUAL_FILE_METHOD_GET_PHYSICAL_FILE = virtualFile.getMethod("getPhysicalFile"); } catch (final NoSuchMethodException ex) { throw new IllegalStateException("Could not detect JBoss VFS infrastructure", ex); } @@ -117,4 +120,14 @@ public class VfsResource implements Resource { return MethodUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_SIZE); } + /** + * 获取物理文件对象 + * + * @return 物理文件对象 + * @since 6.0.0 + */ + public File getFile(){ + return MethodUtil.invoke(virtualFile, VIRTUAL_FILE_METHOD_GET_PHYSICAL_FILE); + } + } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/net/url/URLUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/net/url/URLUtil.java index 318e14619..760455ba4 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/net/url/URLUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/net/url/URLUtil.java @@ -20,7 +20,6 @@ import org.dromara.hutool.core.io.file.FileNameUtil; import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.io.resource.ResourceUtil; import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.net.NetUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.CharsetUtil; @@ -28,17 +27,9 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.JarURLConnection; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; +import java.net.*; import java.nio.charset.Charset; import java.util.Map; -import java.util.jar.JarFile; /** * URL(Uniform Resource Locator)统一资源定位符相关工具类 @@ -52,10 +43,11 @@ import java.util.jar.JarFile; * protocol :// hostname[:port] / path / [:parameters][?query]#fragment * * - * @author xiaoleilu + * @author looly */ public class URLUtil { + // region const /** * 针对ClassPath路径的伪协议前缀(兼容Spring): "classpath:" */ @@ -108,6 +100,7 @@ public class URLUtil { * WAR路径及内部文件路径分界符 */ public static final String WAR_URL_SEPARATOR = "*/"; + // endregion /** * 将{@link URI}转换为{@link URL} @@ -215,6 +208,8 @@ public class URLUtil { } } + // region getURL + /** * 获得URL * @@ -243,14 +238,34 @@ public class URLUtil { * * @param file URL对应的文件对象 * @return URL - * @throws HutoolException MalformedURLException + * @throws IORuntimeException URL格式错误 */ public static URL getURL(final File file) { Assert.notNull(file, "File is null !"); try { return file.toURI().toURL(); } catch (final MalformedURLException e) { - throw new HutoolException(e, "Error occured when get URL!"); + throw new IORuntimeException(e, "Error occurred when get URL!"); + } + } + + /** + * 获取相对于给定URL的新的URL
+ * 来自:org.springframework.core.io.UrlResource#createRelativeURL + * + * @param url 基础URL + * @param relativePath 相对路径 + * @return 相对于URL的子路径URL + * @throws IORuntimeException URL格式错误 + * @since 6.0.0 + */ + public static URL getURL(final URL url, String relativePath) throws HutoolException { + // # 在文件路径中合法,但是在URL中非法,此处转义 + relativePath = StrUtil.replace(StrUtil.removePrefix(relativePath, StrUtil.SLASH), "#", "%23"); + try { + return new URL(url, relativePath); + } catch (final MalformedURLException e) { + throw new IORuntimeException(e, "Error occurred when get URL!"); } } @@ -259,7 +274,7 @@ public class URLUtil { * * @param files URL对应的文件对象 * @return URL - * @throws HutoolException MalformedURLException + * @throws IORuntimeException URL格式错误 */ public static URL[] getURLs(final File... files) { final URL[] urls = new URL[files.length]; @@ -268,11 +283,12 @@ public class URLUtil { urls[i] = files[i].toURI().toURL(); } } catch (final MalformedURLException e) { - throw new HutoolException(e, "Error occured when get URL!"); + throw new IORuntimeException(e, "Error occurred when get URL!"); } return urls; } + // endregion /** * 获取URL中域名部分,只保留URL中的协议(Protocol)、Host,其它为null。 @@ -351,6 +367,8 @@ public class URLUtil { return (null != path) ? path : url.getPath(); } + // region toURI + /** * 转URL为URI * @@ -409,6 +427,7 @@ public class URLUtil { throw new HutoolException(e); } } + // endregion /** * 提供的URL是否为文件
@@ -482,21 +501,7 @@ public class URLUtil { return IoUtil.toReader(getStream(url), charset); } - /** - * 从URL中获取JarFile - * - * @param url URL - * @return JarFile - * @since 4.1.5 - */ - public static JarFile getJarFile(final URL url) { - try { - final JarURLConnection urlConnection = (JarURLConnection) url.openConnection(); - return urlConnection.getJarFile(); - } catch (final IOException e) { - throw new IORuntimeException(e); - } - } + // region normalize /** * 标准化URL字符串,包括: @@ -593,6 +598,7 @@ public class URLUtil { } return protocol + domain + StrUtil.emptyIfNull(path) + StrUtil.emptyIfNull(params); } + // endregion /** * 将Map形式的Form表单数据转换为Url参数形式
@@ -611,31 +617,7 @@ public class URLUtil { return UrlQuery.of(paramMap).build(charset); } - /** - * 获取指定URL对应资源的内容长度,对于Http,其长度使用Content-Length头决定。 - * - * @param url URL - * @return 内容长度,未知返回-1 - * @throws IORuntimeException IO异常 - * @since 5.3.4 - */ - public static long getContentLength(final URL url) throws IORuntimeException { - if (null == url) { - return -1; - } - - URLConnection conn = null; - try { - conn = url.openConnection(); - return conn.getContentLengthLong(); - } catch (final IOException e) { - throw new IORuntimeException(e); - } finally { - if (conn instanceof HttpURLConnection) { - ((HttpURLConnection) conn).disconnect(); - } - } - } + // region getDataUri /** * Data URI Scheme封装,数据格式为Base64。data URI scheme 允许我们使用内联(inline-code)的方式在网页中包含数据,
@@ -708,6 +690,7 @@ public class URLUtil { return builder.toString(); } + // endregion /** * 获取URL对应数据长度 @@ -732,16 +715,21 @@ public class URLUtil { } else { // 如果资源打在jar包中或来自网络,使用网络请求长度 // issue#3226, 来自Spring的AbstractFileResolvingResource + URLConnection conn = null; try { - final URLConnection con = url.openConnection(); - useCachesIfNecessary(con); - if (con instanceof HttpURLConnection) { - final HttpURLConnection httpCon = (HttpURLConnection) con; + conn = url.openConnection(); + useCachesIfNecessary(conn); + if (conn instanceof HttpURLConnection) { + final HttpURLConnection httpCon = (HttpURLConnection) conn; httpCon.setRequestMethod("HEAD"); } - return con.getContentLengthLong(); + return conn.getContentLengthLong(); } catch (final IOException e) { throw new IORuntimeException(e); + } finally { + if (conn instanceof HttpURLConnection) { + ((HttpURLConnection) conn).disconnect(); + } } } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ClassScanner.java b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ClassScanner.java index 500847c37..d83f94788 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ClassScanner.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/reflect/ClassScanner.java @@ -16,14 +16,13 @@ import org.dromara.hutool.core.classloader.ClassLoaderUtil; import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.collection.iter.EnumerationIter; import org.dromara.hutool.core.exception.ExceptionUtil; -import org.dromara.hutool.core.exception.HutoolException; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.file.FileNameUtil; +import org.dromara.hutool.core.io.resource.JarResource; import org.dromara.hutool.core.io.resource.ResourceUtil; import org.dromara.hutool.core.net.url.URLDecoder; -import org.dromara.hutool.core.net.url.URLUtil; -import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.CharUtil; +import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.SystemUtil; @@ -268,7 +267,7 @@ public class ClassScanner implements Serializable { scanFile(new File(URLDecoder.decode(url.getFile(), this.charset)), null); break; case "jar": - scanJar(URLUtil.getJarFile(url)); + scanJar(new JarResource(url).getJarFile()); break; } } diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/AntPathMatcher.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/AntPathMatcher.java index 6c7be9cde..ced09b940 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/AntPathMatcher.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/AntPathMatcher.java @@ -350,7 +350,7 @@ public class AntPathMatcher { pos += skipped; skipped = skipSegment(path, pos, pattDir); if (skipped < pattDir.length()) { - return (skipped > 0 || (pattDir.length() > 0 && isWildcardChar(pattDir.charAt(0)))); + return (skipped > 0 || (!pattDir.isEmpty() && isWildcardChar(pattDir.charAt(0)))); } pos += skipped; } @@ -657,9 +657,9 @@ public class AntPathMatcher { /** - * Tests whether or not a string matches against a pattern via a {@link Pattern}. + * Tests whether a string matches against a pattern via a {@link Pattern}. *

The pattern may contain special characters: '*' means zero or more characters; '?' means one and - * only one character; '{' and '}' indicate a URI template pattern. For example /users/{user}. + * only one character; '{' and '}' indicate a URI template pattern. For example {@code /users/{user}}. */ protected static class AntPathStringMatcher {