URLConnection getContentLength () возвращает отрицательное значение - PullRequest
9 голосов
/ 25 марта 2011

Вот мой код:

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, и его длина отрицательна по следующей причине:

"количество байтовсодержимое или отрицательное число, если оно неизвестно. Если длина содержимого> известна, но превышает Long.MAX_VALUE, возвращается отрицательное число. "

В чем может быть причина этого?Я пытаюсь загрузить изображение.Я хотел бы отметить, что это четвертый способ загрузки изображений.Остальные три упомянуты здесь .

Редактировать:

В соответствии с просьбой, вот полный метод, который я использую.*

Ответы [ 5 ]

26 голосов
/ 17 августа 2014

По умолчанию эта реализация 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

11 голосов
/ 25 марта 2011

Простой ответ - длина содержимого неизвестна.Точнее говоря, сервер не устанавливает заголовок «Content-Length» в ответном сообщении.

Вам придется изменить свой код, чтобы он не выделял заранее байтовый массив фиксированного размера для хранения изображения,

  • Один из вариантов - создать локальный ByteArrayOutputStream и скопировать в него байты, считанные из сокета.Затем вызовите toByteArray, чтобы получить полный байтовый массив.

  • Если вы можете изменить сторону сервера, другой альтернативой является установка заголовка длины содержимого в ответе.Это должно быть сделано ДО того, как вы получите OutputStream для записи байтов изображения в ответ.


Существующий код также нарушен в другом отношении.Если вы получите IOException или другое исключение, блок кода «выйдет ненормально», не закрывая URLConnection.Это приведет к утечке файлового дескриптора.Делайте это слишком много раз, и ваше приложение не сможет работать из-за исчерпания файловых дескрипторов ... или номеров локальных портов.

Рекомендуется использовать try / finally, чтобы убедиться, что URLConnections, Sockets, Streams и т.при этом внешние ресурсы ВСЕГДА закрыты.

3 голосов
/ 06 мая 2014

Похоже, что сервер не предлагает Content-Length в своих заголовках ответа. Вы получили заголовок Transfer-Encoding = chunked из заголовков ответа?

Моя ситуация такова: я выполняю HttpURLConnection и считаю, что сервер ответит мне «Content-Length» с положительным значением, но это не так, затем я перехожу к AndroidHttpClient , в котором реализована реализация Android HttpClient, выполните тот же запрос еще раз и получите право 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

Единственное отличие с заголовком запроса - Accept-Encoding , которое не добавлено мной самостоятельно, это было значение по умолчанию Android для 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

Как вы можете видеть, после того, как я установил Accept-Encoding на «identity», заменил системное значение по умолчанию «gzip», сервер предоставил «Content-Length» положительное значение, поэтому AndroidHttpClient мог принять правильное значение Content-Length и HttpURLCподключения нет.

кодировка сжатия gzip может привести к получению фрагментированного ответа, который учитывается серверной стороной, и если сервер считает, что вы можете получить ответ фрагментированного кодирования, он может не предложить заголовок Content-Length , попробуйте отключить gzip приемлемый поведение тогда посмотрим, какая разница с этим.

0 голосов
/ 14 февраля 2014

Я также сталкиваюсь с этой проблемой в своем приложении обоев. Проблема в том, что ваш сервер не предоставляет Content-Length в своем заголовке http. Вот снимок обычного http-заголовка с Content-length .

enter image description here

Я использую общий ресурс, поэтому не могу изменить конфигурацию сервера. В моем приложении я установил максимальное значение диалога прогресса с приблизительным значением (которое больше моего реального размера файла), например:

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

Вы также можете проверить мой полный пример исходного кода здесь:

Пример диалога прогресса Android .

0 голосов
/ 25 марта 2011

См. Здесь: http://download.oracle.com/javase/6/docs/api/java/net/URLConnection.html#getContentLength%28%29

Здесь указано:

Возвращает:

длина содержимого ресурса, к которому подключено это соединениеURL-ссылки или -1, если длина содержимого неизвестна.

Это просто означает, что заголовок не содержит поля content-length.Если у вас есть контроль над кодом сервера.Вы должны установить контент-длину как-то.Что-то вроде ServletResponse # setContentLength

...