在尝试创建URL时,Java JDK 8中存在异常
在我尝试创建带有嵌套JAR间接寻址的URL时,现在有一个例外 例如,我有以下嵌套jar URL:在尝试创建URL时,Java JDK 8中存在异常,java,url,jar,Java,Url,Jar,在我尝试创建带有嵌套JAR间接寻址的URL时,现在有一个例外 例如,我有以下嵌套jar URL: jar:jar:file:/D://samples/File.zip!/71812_file!/myFile.properties 它是一个带有两个间接地址的嵌套Jar URL 当我尝试执行以下简单行时: URL url = new URL(spec); 我有以下例外: java.net.MalformedURLException: Nested JAR URLs are not support
jar:jar:file:/D://samples/File.zip!/71812_file!/myFile.properties
它是一个带有两个间接地址的嵌套Jar URL
当我尝试执行以下简单行时:
URL url = new URL(spec);
我有以下例外:
java.net.MalformedURLException: Nested JAR URLs are not supported
现在它不再工作了(JDK8_271),但在较旧的JDK8版本中(例如,在我的内存中是92),同样的代码也可以工作
JDK中创建此异常的“违规”代码(以前在JDK代码中不存在)似乎位于sun.net.www.protocol.jar.Handler类中(源代码):
我现在可以做些什么来解决这个问题,新的JDK版本可以吗?好的,所以这里真正的问题是标准Java类加载器不支持嵌套JAR文件,也从来没有支持过嵌套JAR文件。对此,有一个长期存在的功能请求;请参阅 这同样适用于使用
URL.openConnection()
打开嵌套JAR
所发生的事情是JDK 7(!)及其后版本(2019年6月)发生了更改,因此,当您尝试解析URL时,会出现一个明显的异常
如果无法将嵌套JAR提取到文件中,那么解决方案将是为理解嵌套JAR文件的JAR:
查找(或写入)并注册第三方URL协议处理程序类
一种可能是JBossnjar:
现在它不再工作了(JDK8_271),但在较旧的JDK8版本中(例如,在我的内存中是92),同样的代码也可以工作 解析URL可能有效。但是
java.net.URL
类中的注释暗示URL.openConnection
不适用于嵌套的JAR
URL
if ("jar".equalsIgnoreCase(protocol)) {
if (handler instanceof sun.net.www.protocol.jar.Handler) {
// URL.openConnection() would throw a confusing exception
// so generate a better exception here instead.
String s = ((sun.net.www.protocol.jar.Handler) handler).checkNestedProtocol(file);
if (s != null) {
throw new MalformedURLException(s);
}
}
}
嗯,现在我明白了!事实上我已经创建了自己的URLConnection,因为我没有使用url.openConnection代码,这正是您解释的原因(但在此之前,我在尝试创建url时没有出现异常,因此我现在遇到了问题)。我有以下代码:
NestableURLConnection conn = new NestableURLConnection(url);
NestableURLConnection是我创建的自定义URLConnection。我将尝试您的解决方案,这似乎非常合适!我使用的自定义URLConnection与协议处理程序完美配合:
public class NestableURLConnection extends URLConnection {
protected String urlPath;
protected boolean firstEntry = false;
public NestableURLConnection(URL url) {
super(url);
urlPath = url.toString();
}
public NestableURLConnection() {
super(null);
}
public void setURL(URL url) {
this.url = url;
urlPath = url.toString();
}
public NestableURLConnection(URL url, boolean firstEntry) {
this(url);
this.firstEntry = firstEntry;
}
@Override
public void connect() throws IOException {
connected = true;
}
private String getNestedURL() throws IOException {
int sep = urlPath.indexOf("!/");
int start = urlPath.indexOf(':') + 1;
for (int i = start, end = urlPath.indexOf("/") - 1; (i = urlPath.indexOf(":", i)) < end;) {
int cursep = urlPath.indexOf("!/", sep + 2);
if (cursep < 0) {
break;
}
sep = cursep;
++i;
}
return urlPath.substring(start, sep);
}
@Override
public InputStream getInputStream() throws IOException {
int start = urlPath.indexOf(':') + 1;
if (start > urlPath.length() || urlPath.charAt(start) == '/') {
return url.openStream();
}
if (FileUtilities.useFileOrHTTPProtocol(urlPath)) {
return url.openStream();
}
int sep = urlPath.indexOf("!/");
if (sep < 0) {
throw new MalformedURLException("Missing separator " + urlPath);
}
String nestedURL = getNestedURL();
sep = urlPath.indexOf(nestedURL) + nestedURL.length();
int nextSep = urlPath.indexOf("!/", sep + 2);
// Use the default Java openStream() for a file scheme.
InputStream inputStream;
ZipEntry inputZipEntry;
boolean isKnownProtocol = FileUtilities.useFileOrHTTPProtocol(nestedURL);
if (!isKnownProtocol || (firstEntry)) {
if (firstEntry) {
nestedURL = urlPath;
} else {
if (!nestedURL.contains("!/")) {
nestedURL = "file:" + nestedURL.substring(4);
}
}
inputStream = createInputStream(nestedURL);
} else {
String entry = nextSep < 0 ? urlPath.substring(sep + 2) : urlPath.substring(sep + 2, nextSep);
sep = nextSep;
nextSep = urlPath.indexOf("!/", sep + 2);
// Go directly to the right entry in the zip file
final ZipFile zipFile = new ZipFile(FileUtilities.replaceEscapedSequences(nestedURL.substring(5)));
inputZipEntry = zipFile.getEntry(entry);
InputStream zipEntryInputStream = inputZipEntry == null ? null : zipFile.getInputStream(inputZipEntry);
if (zipEntryInputStream == null) {
zipFile.close();
// return null instead of throwing an Exception if the URL does not exist
return null;
}
inputStream = new FilterInputStream(zipEntryInputStream) {
@Override
public void close() throws IOException {
super.close();
zipFile.close();
}
};
}
// Loop over the archive paths.
LOOP:
while (sep > 0) {
// The entry name to be matched.
String entry = nextSep < 0 ? urlPath.substring(sep + 2) : urlPath.substring(sep + 2, nextSep);
// Wrap the input stream as a zip stream to scan it's contents for a match.
// Don't use try-with-resources here because we only want to close it if there is an internal IOException
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
while (zipInputStream.available() >= 0) {
ZipEntry zipEntry = zipInputStream.getNextEntry();
if (zipEntry == null) {
break;
} else if (firstEntry) {
inputZipEntry = zipEntry;
inputStream = zipInputStream;
sep = -1;
continue LOOP;
} else if (entry.equals(zipEntry.getName())) {
inputZipEntry = zipEntry;
inputStream = zipInputStream;
// Skip to the next archive path and continue the loop.
sep = nextSep;
nextSep = urlPath.indexOf("!/", sep + 2);
continue LOOP;
}
}
throw new IOException("Archive entry not found " + urlPath);
}
return inputStream;
}
private InputStream createInputStream(String nestedURL) throws IOException {
return new URL(nestedURL).openStream();
}
@Override
public OutputStream getOutputStream() throws IOException {
return null;
}
公共类NestableURLConnection扩展了URLConnection{
受保护的字符串路径;
受保护的布尔值firstEntry=false;
公共嵌套URL连接(URL){
超级链接(url);
urlPath=url.toString();
}
公共NestableURLConnection(){
超级(空);
}
公共无效设置URL(URL){
this.url=url;
urlPath=url.toString();
}
公共NestableURLConnection(URL,布尔值firstEntry){
此(url);
this.firstEntry=firstEntry;
}
@凌驾
public void connect()引发IOException{
连接=真;
}
私有字符串getNestedURL()引发IOException{
int sep=urlPath.indexOf(!/”;
int start=urlPath.indexOf(“:”)+1;
对于(int i=start,end=urlPath.indexOf(“/”)-1;(i=urlPath.indexOf(“:”,i))urlPath.length()| | urlPath.charAt(开始)=='/')){
返回url.openStream();
}
if(FileUtilities.useFileOrHTTPProtocol(urlPath)){
返回url.openStream();
}
int sep=urlPath.indexOf(!/”;
如果(sep<0){
抛出新的MalformedURLException(“缺少分隔符”+urlPath);
}
字符串nestedURL=getNestedURL();
sep=urlPath.indexOf(nestedURL)+nestedURL.length();
int-nextSep=urlPath.indexOf(“!/”,sep+2);
//对文件方案使用默认的Java openStream()。
输入流输入流;
Zippentry输入Zippentry;
布尔值isKnownProtocol=FileUtilities.useFileOrHTTPProtocol(nestedURL);
如果(!isKnownProtocol | | |(第一项)){
如果(首次输入){
nestedURL=urlPath;
}否则{
如果(!nestedURL.contains(!/”){
nestedURL=“文件:”+nestedURL.substring(4);
}
}
inputStream=createInputStream(nestedURL);
}否则{
String entry=nextSep<0?urlPath.substring(sep+2):urlPath.substring(sep+2,nextSep);
sep=nextSep;
nextSep=urlPath.indexOf(“!/”,sep+2);
//直接转到zip文件中的正确条目
final ZipFile ZipFile=新的ZipFile(FileUtilities.replaceScapedSequences(nestedURL.substring(5));
inputZipEntry=zipFile.getEntry(条目);
InputStream ZipPentryInputStream=InputZipPentry==null?null:zipFile.getInputStream(InputZipPentry);
if(zipEntryInputStream==null){
zipFile.close();
//如果URL不存在,则返回null而不是引发异常
返回null;
}
inputStream=新过滤器inputStream(zipEntryInputStream){
@凌驾
public void close()引发IOException{
super.close();
zipFile.close();
}
};
}
//在归档路径上循环。
循环:
而(9月>0日){
//要匹配的条目名称。
String entry=nextSep<0?urlPath.substring(sep+2):urlPath.substring(sep+2,nextSep);
//将输入流包装为zip流,以扫描其内容以查找匹配项。
//这里不要使用try with resources,因为我们只想在出现内部IOException时关闭它
ZipInputStream ZipInputStream=新的ZipInputStream(inputStream);
而(zipInputStream.available()>=0){
ZipEntry-ZipEntry=zipInputStream.getNextEntry();
if(zipEntry==null){
public class NestableURLConnection extends URLConnection {
protected String urlPath;
protected boolean firstEntry = false;
public NestableURLConnection(URL url) {
super(url);
urlPath = url.toString();
}
public NestableURLConnection() {
super(null);
}
public void setURL(URL url) {
this.url = url;
urlPath = url.toString();
}
public NestableURLConnection(URL url, boolean firstEntry) {
this(url);
this.firstEntry = firstEntry;
}
@Override
public void connect() throws IOException {
connected = true;
}
private String getNestedURL() throws IOException {
int sep = urlPath.indexOf("!/");
int start = urlPath.indexOf(':') + 1;
for (int i = start, end = urlPath.indexOf("/") - 1; (i = urlPath.indexOf(":", i)) < end;) {
int cursep = urlPath.indexOf("!/", sep + 2);
if (cursep < 0) {
break;
}
sep = cursep;
++i;
}
return urlPath.substring(start, sep);
}
@Override
public InputStream getInputStream() throws IOException {
int start = urlPath.indexOf(':') + 1;
if (start > urlPath.length() || urlPath.charAt(start) == '/') {
return url.openStream();
}
if (FileUtilities.useFileOrHTTPProtocol(urlPath)) {
return url.openStream();
}
int sep = urlPath.indexOf("!/");
if (sep < 0) {
throw new MalformedURLException("Missing separator " + urlPath);
}
String nestedURL = getNestedURL();
sep = urlPath.indexOf(nestedURL) + nestedURL.length();
int nextSep = urlPath.indexOf("!/", sep + 2);
// Use the default Java openStream() for a file scheme.
InputStream inputStream;
ZipEntry inputZipEntry;
boolean isKnownProtocol = FileUtilities.useFileOrHTTPProtocol(nestedURL);
if (!isKnownProtocol || (firstEntry)) {
if (firstEntry) {
nestedURL = urlPath;
} else {
if (!nestedURL.contains("!/")) {
nestedURL = "file:" + nestedURL.substring(4);
}
}
inputStream = createInputStream(nestedURL);
} else {
String entry = nextSep < 0 ? urlPath.substring(sep + 2) : urlPath.substring(sep + 2, nextSep);
sep = nextSep;
nextSep = urlPath.indexOf("!/", sep + 2);
// Go directly to the right entry in the zip file
final ZipFile zipFile = new ZipFile(FileUtilities.replaceEscapedSequences(nestedURL.substring(5)));
inputZipEntry = zipFile.getEntry(entry);
InputStream zipEntryInputStream = inputZipEntry == null ? null : zipFile.getInputStream(inputZipEntry);
if (zipEntryInputStream == null) {
zipFile.close();
// return null instead of throwing an Exception if the URL does not exist
return null;
}
inputStream = new FilterInputStream(zipEntryInputStream) {
@Override
public void close() throws IOException {
super.close();
zipFile.close();
}
};
}
// Loop over the archive paths.
LOOP:
while (sep > 0) {
// The entry name to be matched.
String entry = nextSep < 0 ? urlPath.substring(sep + 2) : urlPath.substring(sep + 2, nextSep);
// Wrap the input stream as a zip stream to scan it's contents for a match.
// Don't use try-with-resources here because we only want to close it if there is an internal IOException
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
while (zipInputStream.available() >= 0) {
ZipEntry zipEntry = zipInputStream.getNextEntry();
if (zipEntry == null) {
break;
} else if (firstEntry) {
inputZipEntry = zipEntry;
inputStream = zipInputStream;
sep = -1;
continue LOOP;
} else if (entry.equals(zipEntry.getName())) {
inputZipEntry = zipEntry;
inputStream = zipInputStream;
// Skip to the next archive path and continue the loop.
sep = nextSep;
nextSep = urlPath.indexOf("!/", sep + 2);
continue LOOP;
}
}
throw new IOException("Archive entry not found " + urlPath);
}
return inputStream;
}
private InputStream createInputStream(String nestedURL) throws IOException {
return new URL(nestedURL).openStream();
}
@Override
public OutputStream getOutputStream() throws IOException {
return null;
}