URLConnection getContentLength() is returning a negative value

依然范特西╮ 提交于 2019-11-26 08:32:44

问题


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:

\"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.\"

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

回答1:


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.

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. Then call toByteArray to grab the full byte array.

  • 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.


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. 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.




回答2:


By default, this implementation of HttpURLConnection requests that servers use gzip compression.
Since getContentLength() returns the number of bytes transmitted, you cannot use that method to predict how many bytes can be read from getInputStream().
Instead, read that stream until it is exhausted: when read() returns -1.
Gzip compression can be disabled by setting the acceptable encodings in the request header:

 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




回答3:


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?

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.

I used Wireshark to analysis the two requests, found a little difference about request headers.

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

---------------------------------- 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 :

---------------------------------- 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.

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.




回答4:


See here: 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.

It simply means header does not contains the content-length field. If you are having control over server code. You should set the content-lenth somehow. Something like ServletResponse#setContentLength




回答5:


I also meet this problem in my Wallpaper app. The problem is because your server doesn't provide Content-Length in its http header. Here is a snapshot about a normal http header with Content-length.

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.



来源:https://stackoverflow.com/questions/5428639/urlconnection-getcontentlength-is-returning-a-negative-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!