我遇到了以下问题:当通过代理使用URLConnection
时,内容长度始终设置为-1
。
首先,我检查了该代理确实返回Content-Length
(lynx
和wget
也通过代理工作;没有其他方法可以从本地网络上网):
$ lynx -source -head ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip
HTTP/1.1 200 OK
Last-Modified: Mon, 09 Jul 2007 17:02:37 GMT
Content-Type: application/x-zip-compressed
Content-Length: 30745
Connection: close
Date: Thu, 02 Feb 2012 17:18:52 GMT
$ wget -S -X HEAD ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip
--2012-04-03 19:36:54-- ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip
Resolving proxy... 10.10.0.12
Connecting to proxy|10.10.0.12|:8080... connected.
Proxy request sent, awaiting response...
HTTP/1.1 200 OK
Last-Modified: Mon, 09 Jul 2007 17:02:37 GMT
Content-Type: application/x-zip-compressed
Content-Length: 30745
Connection: close
Age: 0
Date: Tue, 03 Apr 2012 17:36:54 GMT
Length: 30745 (30K) [application/x-zip-compressed]
Saving to: `WO2003-104476-001.zip'
在Java中我写道:
URL url = new URL("ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip");
int length = url.openConnection().getContentLength();
logger.debug("Got length: " + length);
我得到-1
。我开始调试FtpURLConnection
,结果发现必要的信息位于基础HttpURLConnection.responses
字段中,但从那时起它从未正确填充:
(标题中有Content-Length: 30745
)。当您开始读取流时或甚至在读取流之后,内容长度不会更新。代码:
URL url = new URL("ftp://ftp.wipo.int/pub/published_pct_sequences/publication/2003/1218/WO03_104476/WO2003-104476-001.zip");
URLConnection connection = url.openConnection();
logger.debug("Got length (1): " + connection.getContentLength());
InputStream input = connection.getInputStream();
byte[] buffer = new byte[4096];
int count = 0, len;
while ((len = input.read(buffer)) > 0) {
count += len;
}
logger.debug("Got length (2): " + connection.getContentLength() + " but wanted " + count);
输出:
Got length (1): -1
Got length (2): -1 but wanted 30745
似乎它是JDK6中的一个错误,所以我打开了新的bug#7168608。
file:/
网址进行FTP连接,我将不胜感激。答案 0 :(得分:2)
请记住,代理通常会更改基础实体的表示形式。在您的情况下,我怀疑代理可能正在改变传输编码。这反过来又使得Content-Length即使提供也毫无意义。
您违反了HTTP 1.1规范的以下两个部分:
- ...
- ...
- 如果存在Content-Length头字段(第14.13节),则其在OCTET中的十进制值表示实体长度和传输长度。如果这两个长度不同(即,如果存在Transfer-Encoding头字段),则不得发送Content-Length头字段。如果收到带有Transfer-Encoding标头字段和Content-Length标头字段的消息,则必须忽略后者。
醇>Transfer-Encoding通用标头字段指示已对邮件正文应用了哪种(如果有)转换类型,以便在发件人和收件人之间安全地进行转换。这与内容编码的不同之处在于,传输编码是消息的属性,而不是实体的属性。
Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
转移编码在第3.6节中定义。一个例子是:
Transfer-Encoding: chunked
如果对实体应用了多种编码,则必须按照应用顺序列出转移编码。有关编码参数的其他信息可以由本规范未定义的其他实体头字段提供。
许多较旧的HTTP / 1.0应用程序无法理解Transfer-Encoding标头。
因此,根据规范,URLConnection会忽略Content-Length
标题,因为在存在分块传输时它没有意义
在您的调试器屏幕截图中,不清楚Transfer-Encoding
标头是否存在。请告诉我们......
进一步调查 - lynx似乎没有显示您发出lynx -head
时返回的所有标头。它没有显示Transfer-Encoding
标题对此讨论至关重要。
以下是公开可见网站与差异的证明
Ξ▶ lynx -useragent='dummy' -source -head http://www.bbc.co.uk
HTTP/1.1 302 Found
Server: Apache
X-Cache-Action: PASS (non-cacheable)
X-Cache-Age: 0
Content-Type: text/html; charset=iso-8859-1
Date: Tue, 03 Apr 2012 13:33:06 GMT
Location: http://www.bbc.co.uk/mobile/
Connection: close
Ξ▶ wget -useragent='dummy' -S -X HEAD http://www.bbc.co.uk
--2012-04-03 14:33:22-- http://www.bbc.co.uk/
Resolving www.bbc.co.uk... 212.58.244.70
Connecting to www.bbc.co.uk|212.58.244.70|:80... connected.
HTTP request sent, awaiting response...
HTTP/1.1 200 OK
Server: Apache
Cache-Control: private, max-age=15
Etag: "7e0f292b2e5e4c33cac1bc033779813b"
Content-Type: text/html
Transfer-Encoding: chunked
Date: Tue, 03 Apr 2012 13:33:22 GMT
Connection: keep-alive
X-Cache-Action: MISS
X-Cache-Age: 0
X-LB-NoCache: true
Vary: Cookie
由于我显然不在您的网络中,因此我无法复制您的具体情况,但请确认您确实在通过时未获得Transfer-Encoding标头代理。
答案 1 :(得分:1)
我认为这是与处理代理的ftp连接相关的jdk中的“bug”。当代理正在使用时,FtpURLConnection委托给HttpURLConnection。 然而,在这种情况下,FtpURLConnection似乎不会将任何标头管理委托给此HttpURLConnection。因此,您可以正确获取流,但我认为您不能访问任何“标题”值,如内容长度或内容类型。 (这是基于对1.5的openjdk源的快速浏览,我本可以错过一些东西)。
答案 2 :(得分:0)
我要做的一件事就是实际阅读回复(写下我的头脑所以期待错误):
URLConnection connection= url.openConnection();
InputStream input= connection.getInputStream();
byte[] buffer= new byte[4096];
while(input.read(buffer) > 0)
;
logger.debug("Got length: " + getContentLength());
如果您获得的大小良好,那么寻找一种方法让URLConnection读取标题而不是数据以避免读取整个响应。