简体   繁体   English

"URLConnection getContentLength() 返回负值"

[英]URLConnection getContentLength() is returning a negative value

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;

I looked at this in Java documentation and the length is negative because of the following reason:我在 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." “内容的字节数,如果未知,则为负数。如果内容>长度已知但超过 Long.MAX_VALUE,则返回负数。”

What might be the reason for this?这可能是什么原因? I am trying to download an image.我正在尝试下载图像。 I would like to point out that this is the fourth method that I am trying to download images.我想指出,这是我尝试下载图像的第四种方法。 The other three are mentioned here .其他三个在这里提到。

Edit:编辑:

As requested, here is the full method I am using.根据要求,这是我正在使用的完整方法。

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

By default, this implementation of HttpURLConnection requests that servers use gzip compression. 默认情况下,HttpURLConnection的此实现请求服务器使用gzip压缩。
Since getContentLength() returns the number of bytes transmitted, you cannot use that method to predict how many bytes can be read from getInputStream(). 由于getContentLength()返回传输的字节数,因此您无法使用该方法来预测可从getInputStream()读取的字节数。
Instead, read that stream until it is exhausted: when read() returns -1. 相反,读取该流直到它耗尽:当read()返回-1时。
Gzip compression can be disabled by setting the acceptable encodings in the request header: 可以通过在请求标头中设置可接受的编码来禁用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

Source (Performance paragraph): http://developer.android.com/reference/java/net/HttpURLConnection.html 来源(绩效段落): http//developer.android.com/reference/java/net/HttpURLConnection.html

The simple answer is that the content length is not known. 简单的答案是内容长度未知。 Or more specifically, the server is not setting a "Content-Length" header in the response message. 或者更具体地说,服务器没有在响应消息中设置“Content-Length”头。

You will have to change your code so that it doesn't preallocate a fixed sized byte array to hold the image. 您将不得不更改代码,以便它不预先分配固定大小的字节数组来保存图像。

  • One alternative is to create a local ByteArrayOutputStream and copy bytes read from the socket to that. 另一种方法是创建一个本地ByteArrayOutputStream并将从套接字读取的字节复制到该字节。 Then call toByteArray to grab the full byte array. 然后调用toByteArray来获取完整的字节数组。

  • If you can change the server side, another alternative is to set the content length header in the response. 如果您可以更改服务器端,另一种方法是在响应中设置内容长度标头。 This has to be done BEFORE you get the OutputStream to write the image bytes to the response. 必须在使OutputStream将图像字节写入响应之前完成此操作。


You existing code is broken in another respect as well. 您现有的代码也在另一个方面被打破。 If you get an IOException or some other exception, the code block will "exit abnormally" without closing the URLConnection. 如果您收到IOException或其他异常,代码块将“异常退出”而不关闭URLConnection。 This will result in the leakage of a file descriptor. 这将导致文件描述符的泄漏。 Do this too many times and your application will fail due to exhaustion of file descriptors ... or local port numbers. 执行此操作太多次,并且您的应用程序将因文件描述符...或本地端口号耗尽而失败。

It is best practice to use a try / finally to ensure that URLConnections, Sockets, Streams and so on that tie down external resources are ALWAYS closed. 最佳做法是使用try / finally确保始终关闭外部资源的URLConnections,Sockets,Streams等。

It seems the server doesn't offer Content-Length in its response headers, did you get the Transfer-Encoding=chunked header from the response headers? 似乎服务器在其响应头中没有提供Content-Length ,你是否从响应头中获得了Transfer-Encoding = chunked头?

My situation is : I perform a HttpURLConnection and consider the server would response to me the "Content-Length" with positive value, but it didn't, then I turn to the AndroidHttpClient which android HttpClient implementation, perform the same request again and got the right Content-Length . 我的情况是:我执行一个HttpURLConnection,并考虑服务器会响应我的“内容长度”具有正值,但它没有,然后我转向AndroidHttpClient其中的Android HttpClient实现,再次执行相同的请求并获得正确的内容长度

I used Wireshark to analysis the two requests, found a little difference about request headers. 我使用Wireshark来分析这两个请求,发现请求头有点不同。

the header list that use AndroidHttpClient : 使用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

the request header list that use HttpURLConnection : 使用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

The only difference with request header is Accept-Encoding which isn't added by myself, it was Android default setting for HttpURLConnection , after that, I set it to identity then perform request again, below is the full header stacks : 与请求标头的唯一区别是Accept-Encoding ,它不是我自己添加的,它是HttpURLConnection的 Android默认设置,之后,我将其设置为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 Accept-Encoding to "identity" replace system default value "gzip", the server provided "Content-Length" positive, that's why AndroidHttpClient could take the right value of Content-Length and HttpURLConnection not. 正如您所看到的,在我将Accept-Encoding设置为“identity”替换系统默认值“gzip”之后,服务器提供了“Content-Length”为正,这就是为什么AndroidHttpClient可以采用Content-Length和HttpURLConnection的正确值。

the gzip compression encoding may cause chunked response that consider by server-side, and if server think you can receive chunked encoding response, it may not offer the Content-Length header, try to disable gzip acceptable behavior then see what difference with that. gzip压缩编码可能导致服务器端考虑的分块响应,如果服务器认为你可以接收分块编码响应,它可能不提供Content-Length头,尝试禁用gzip可接受的行为,然后看看有什么不同。

I also meet this problem in my Wallpaper app. 我也在我的壁纸应用中遇到了这个问题。 The problem is because your server doesn't provide Content-Length in its http header. 问题是因为您的服务器不在其http标头中提供Content-Length Here is a snapshot about a normal http header with Content-length . 以下是有关Content-length的普通http标头的快照。

在此输入图像描述

I am using a share host so I can not change the server configuration. 我正在使用共享主机,因此无法更改服务器配置。 In my app, I am set progress dialog max value with an approximated value (which is bigger than my real file size) like this: 在我的应用程序中,我设置了进度对话框最大值,其值为近似值(大于我的实际文件大小),如下所示:

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

You can also check my full example source code here: 您还可以在此处查看我的完整示例源代码:

Android Progress Dialog Example . Android进度对话框示例

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

It says, 它说,

Returns: 返回:

the content length of the resource that this connection's URL references, or -1 if the content length is not known. 此连接的URL引用的资源的内容长度,如果内容长度未知,则为-1。

It simply means header does not contains the content-length field. 它只是表示标题不包含content-length字段。 If you are having control over server code. 如果您可以控制服务器代码。 You should set the content-lenth somehow. 你应该以某种方式设置内容长度。 Something like ServletResponse#setContentLength ServletResponse#setContentLength之类的东西

I am late here but this might help someone.我在这里迟到了,但这可能会对某人有所帮助。 I was facing same issue i was always getting -1<\/strong> value, when ever i was trying get the content length.我遇到了同样的问题,当我尝试获取内容长度时,我总是得到-1<\/strong>值。

previously i was using below method to get content length.以前我使用下面的方法来获取内容长度。

long totalByte=connection.getContentLength();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM