URLConnection getContentLength()返回一个负值

10 人关注

Here is my code:

url = paths[0];
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int length = connection.getContentLength(); // i get negetive length
InputStream is = (InputStream) url.getContent();
byte[] imageData = new byte[length]; 
int buffersize = (int) Math.ceil(length / (double) 100);
int downloaded = 0;
int read;
while (downloaded < length) {
    if (length < buffersize) {
        read = is.read(imageData, downloaded, length);
    } else if ((length - downloaded) <= buffersize) {
        read = is.read(imageData, downloaded, length - downloaded);
    } else {
        read = is.read(imageData, downloaded, buffersize);
    downloaded += read;
    publishProgress((downloaded * 100) / length);
Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0,
        length);
if (bitmap != null) {
    Log.i(TAG, "Bitmap created");
} else {
    Log.i(TAG, "Bitmap not created");
is.close();
return bitmap;

我在Java文档中看了一下,长度是负的,因为有以下原因。

"the number of bytes of the content, or a negative number if unknown. If the content >length is known but exceeds Long.MAX_VALUE, a negative number is returned."

这可能是什么原因?我正试图下载一张图片。我想指出的是,这是我试图下载图像的第四种方法。其他三种方法都提到了here.

Edit:

按照要求,这里是我使用的全部方法。

protected Bitmap getImage(String imgurl) {
    try {
        URL url = new URL(imgurl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        int length = connection.getContentLength();
        InputStream is = (InputStream) url.getContent();
        byte[] imageData = new byte[length];
        int buffersize = (int) Math.ceil(length / (double) 100);
        int downloaded = 0;
        int read;
        while (downloaded < length) {
            if (length < buffersize) {
                read = is.read(imageData, downloaded, length);
            } else if ((length - downloaded) <= buffersize) {
                read = is.read(imageData, downloaded, length
                        - downloaded);
            } else {
                read = is.read(imageData, downloaded, buffersize);
            downloaded += read;
        //  publishProgress((downloaded * 100) / length);
        Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0,length);
        if (bitmap != null) {
             System.out.println("Bitmap created");
        } else {
            System.out.println("Bitmap not created");
        is.close();
        return bitmap;
    } catch (MalformedURLException e) {
        System.out.println(e);
    } catch (IOException e) {
        System.out.println(e);
    } catch (Exception e) {
        System.out.println(e);
    return null;
    
java
android
httpurlconnection
pradeep
pradeep
发布于 2011-03-25
6 个回答
Jérôme Teisseire
Jérôme Teisseire
发布于 2021-10-12
已采纳
0 人赞同

默认情况下,HttpURLConnection的这个实现要求服务器使用gzip压缩。
由于getContentLength()返回传输的字节数,你不能用这个方法来预测从getInputStream()可以读到多少字节。
相反,读取该流直到它被耗尽:当read()返回-1。
可以通过在请求头中设置可接受的编码来禁用Gzip压缩。

 urlConnection.setRequestProperty("Accept-Encoding", "identity");

So try this:

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Accept-Encoding", "identity"); // <--- Add this line
int length = connection.getContentLength(); // i get negetive length

来源(绩效段)。http://developer.android.com/reference/java/net/HttpURLConnection.html

谢谢,它是完美的答案 :)
哇!这很有效。我正准备扔掉我的笔记本电脑。
Stephen C
Stephen C
发布于 2021-10-12
0 人赞同

对此可能有两种常见的解释。

  • 内容的长度不知道。 或者更具体地说,服务器没有在响应信息中设置 "Content-Length "头。

  • 内容长度大于 Integer.MAX_VALUE 。 如果发生这种情况, getContentLength() 会返回 -1 。(javadocs建议使用 getContentLengthLong() 而不是 getContentLength() 来避免这个问题)。

    无论如何,最好不要预先分配一个固定大小的字节数来存放图像。

  • 一种替代方法是创建一个本地的 ByteArrayOutputStream ,并将从套接字中读取的字节复制到其中。 然后调用 toByteArray 来获取完整的字节数组。

  • 另一个选择是将数据保存在文件系统的一个临时文件中。

    显然,造成这种情况的一个常见的根本原因是,一些实现会默认要求对响应数据进行 "gzip "编码。 这迫使服务器将内容长度设置为-1。 你可以像这样防止这种情况。

    connection.setRequestProperty("Accept-Encoding", "identity");
    

    ...但这意味着响应不会被压缩。 所以,这就是 (IMO)是一个不合格的解决方案。

    你现有的客户端代码在另一个方面也有问题。 如果你得到一个IOException或其他异常,代码块将 "异常退出 "而不关闭URLConnection。 这将导致一个文件描述符的泄漏。 这样做的次数多了,你的应用程序就会因为文件描述符的耗尽而失败......或本地端口号。

    最好的做法是使用try / finally来确保绑定外部资源的URLConnections、Sockets、Streams等总是被关闭。

    根据(声称的)内容长度预先分配一个缓冲区,会使你受到拒绝服务攻击。 想象一下,如果坏人向你发送大量带有危险的大 "Content-Length "头的请求,然后慢速发送数据,你会怎样? OOMEs或更糟。

  • 你建议我在应用程序或服务器端做的任何改变。我需要在这里添加的任何代码
    我确实是在捕捉异常,我并没有只贴出上面的代码,上面所有的代码都在 try catch 语句中。
    这不仅仅是一个捕捉异常的问题。 你还必须在最终块中关闭连接。
    你好,我已经添加了我在编辑中使用的方法的完整代码。
    是的,我明白了。 正如我所预料的那样(正如我在上面解释的那样),如果在打开连接和关闭InputStream之间抛出一个异常,你的代码就有可能泄露一个套接字文件描述符。
    VinceStyling
    VinceStyling
    发布于 2021-10-12
    0 人赞同

    It seems the server doesn't offer Content-Length 在其响应标题中,你是否得到了 Transfer-Encoding=chunked 响应头中的头?

    我的情况是:我执行一个 HttpURLConnection 并认为服务器会给我的 "Content-Length "回复正值,但它没有,然后我转向了 的问题。 浏览器 实现,再次执行同样的请求并得到正确的结果。 Content-Length .

    我用Wireshark分析了这两个请求,发现请求头有一点不同。

    the header list that use 浏览器 :

    ---------------------------------- request headers
    Range : bytpe=0-
    Host : download.game.yy.com
    Connection : Keep-Alive
    User-Agent : com.duowan.mobile.netroid
    ---------------------------------- response headers
    Server : nginx
    Content-Type : text/plain; charset=utf-8
    ETag : "535e2578-84e350"
    Cache-Control : max-age=86400
    Accept-Ranges : bytes
    Content-Range : bytes 0-8708943/8708944
    Content-Length : 8708944
    

    在请求头列表中使用HttpURLConnection。

    ---------------------------------- request headers
    Range : bytpe=0-
    Host : download.game.yy.com
    Connection : Keep-Alive
    User-Agent : com.duowan.mobile.netroid
    Accept-Encoding : gzip    // difference here
    ---------------------------------- response headers
    Server : nginx
    Content-Type : text/plain; charset=utf-8
    Cache-Control : max-age=86400
    Transfer-Encoding : chunked
    X-Android-Received-Millis : 1398861612782
    X-Android-Sent-Millis : 1398861608538
    

    与请求头的唯一区别是接受-编码这不是我自己添加的,是安卓系统的默认设置。HttpURLConnection,之后,我把它设置为identity然后再次执行请求,下面是完整的头堆栈。

    ---------------------------------- request headers
    Range : bytpe=0-
    Host : download.game.yy.com
    Connection : Keep-Alive
    User-Agent : com.duowan.mobile.netroid
    Accept-Encoding : identity
    ---------------------------------- response headers
    Server : nginx
    Content-Type : text/plain; charset=utf-8
    ETag : "535e2578-84e350"
    Cache-Control : max-age=86400
    Accept-Ranges : bytes
    Content-Range : bytes 0-8708943/8708944
    Content-Length : 8708944
    X-Android-Received-Millis : 1398862186902
    X-Android-Sent-Millis : 1398862186619
    

    as you can see, after I set the 接受-编码 to "identity" replace system default value "gzip", the server provided "Content-Length" positive, that's why 浏览器 could take the right value of Content-Length and HttpURLConnection not.

    gzip压缩编码可能导致服务器端考虑的分块响应,如果服务器认为你能收到分块编码的响应,它可能不会提供Content-Length头部,尝试禁用gzip可接受的行为,然后看看有什么不同。

    请注意:关闭传输编码可能会导致服务器的性能问题,因为很可能它不能再 "流 "你要求的东西,而必须在发送前 "计算 "整个响应。最好的计划是不依赖该头文件,而只是流式传输。 这与压缩等无关。
    Nishant
    Nishant
    发布于 2021-10-12
    0 人赞同

    See here: http://download.oracle.com/javase/6/docs/api/java/net/URLConnection.html#getContentLength%28%29

    Returns:

    这个连接的URL引用的资源的内容长度,如果内容长度不知道,则为-1。

    它只是意味着头文件不包含 content-length 字段。如果你对服务器代码有控制权。你应该以某种方式设置content-lenth。比如说 ServletResponse#setContentLength

    这个连接的URL引用的资源的内容长度,如果内容长度不知道,则为-1。
    James
    James
    发布于 2021-10-12
    0 人赞同

    我在我的墙纸应用程序中也遇到了这个问题。问题是由于你的服务器没有提供 Content-Length 在其http头中。下面是一个关于普通http头的快照,其中有 Content-length .

    我使用的是共享主机,所以我不能改变服务器的配置。在我的应用程序中,我用一个近似值(比我的真实文件大小要大)来设置进度对话框的最大值,像这样。

    int filesize = connection.getContentLength();
    if(filesize < 0) {
        progressDialog.setMax(1000000);
    } else {
        progressDialog.setMax(filesize);
    

    你也可以在这里查看我的完整示例源代码。

    安卓进度对话框示例.

    amit semwal
    amit semwal
    发布于 2021-10-12
    0 人赞同

    我在这里迟到了,但这可能有助于某人。我也面临同样的问题,我总是收到 -1 值,当我试图获得内容的长度时。

    以前,我使用下面的方法来获取内容的长度。