URLConnection getContentLength()返回负值

时间:2011-03-25 04:45:25

标签: java android httpurlconnection

这是我的代码:

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文档中查看了这个,由于以下原因,长度为负:

  

“内容的字节数,如果未知,则为负数。如果内容&gt;长度已知但超过Long.MAX_VALUE,则返回负数。”

这可能是什么原因?我正在尝试下载图像。我想指出这是我尝试下载图像的第四种方法。其他三个被提及here

修改:

根据要求,这是我正在使用的完整方法。

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;
}

5 个答案:

答案 0 :(得分:24)

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

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

所以试试这个:

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

答案 1 :(得分:11)

简单的答案是内容长度未知。或者更具体地说,服务器没有在响应消息中设置“Content-Length”标头。

您必须更改代码,以便它不预先分配固定大小的字节数组来保存图像。

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

  • 如果您可以更改服务器端,另一种方法是在响应中设置内容长度标头。必须在将OutputStream写入响应之前完成此操作。


您现有的代码也在另一方面被打破。如果您收到IOException或其他异常,代码块将“异常退出”而不关闭URLConnection。这将导致文件描述符的泄漏。执行此操作太多次,并且由于文件描述符...或本地端口号耗尽,您的应用程序将失败。

最佳做法是使用try / finally确保关闭外部资源的URLConnections,Sockets,Streams等始终关闭。

答案 2 :(得分:3)

服务器似乎没有在其响应标头中提供 Content-Length ,您是否从响应标头中获得了 Transfer-Encoding = chunked 标头?

我的情况是:我执行 HttpURLConnection 并认为服务器会回复我&#34; Content-Length&#34;具有正值,但它没有,然后我转向 Android HttpClient实现的 AndroidHttpClient ,再次执行相同的请求并获得正确的 Content-Length

我使用Wireshark来分析这两个请求,发现请求标题略有不同。

使用AndroidHttpClient的标头列表:

---------------------------------- 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的Android默认设置,之后,我将其设置为< strong> 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

正如您所看到的,在我将Accept-Encoding设置为&#34; identity&#34;替换系统默认值&#34; gzip&#34;,服务器提供&#34;内容长度&#34;积极的,这就是为什么AndroidHttpClient可以采用Content-Length和HttpURLConnection的正确值。

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

答案 3 :(得分:0)

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

它说,

  

<强>返回:

     

此连接的URL引用的资源的内容长度,如果内容长度未知,则为-1。

它只是表示标题不包含content-length字段。如果您可以控制服务器代码。你应该以某种方式设置内容长度。像ServletResponse#setContentLength

这样的东西

答案 4 :(得分:0)

我也在我的壁纸应用中遇到了这个问题。问题是因为您的服务器在其http标头中未提供 Content-Length 。以下是有关内容长度的正常http标头的快照。

enter image description here

我正在使用共享主机,因此无法更改服务器配置。在我的应用程序中,我设置了进度对话框最大值,其值为近似值(大于我的实际文件大小),如下所示:

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

您还可以在此处查看我的完整示例源代码:

Android Progress Dialog Example