10
votes

URLConnection GetContenTlength () renvoie une valeur négative

Voici mon code:

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


0 commentaires

5 Réponses :


13
votes

La réponse simple est que la longueur du contenu est inconnu. Ou plus définissant précisément, le serveur est pas un en-tête « Content-Length » dans le message de réponse.

Vous devez changer votre code afin qu'il ne Préallouer pas un tableau d'octets de taille fixe pour contenir l'image.

  • Une alternative est de créer local ByteArrayOutputStream et copiez octets lus dans la prise à ce sujet. Ensuite, appelez toByteArray pour saisir le tableau d'octets complet.

  • Si vous pouvez changer le côté serveur, une autre alternative est de définir l'en-tête de la longueur du contenu de la réponse. Cela doit être fait avant d'obtenir le OutputStream pour écrire l'image octets à la réponse.


    Vous le code existant est cassé dans un autre rapport aussi bien. Si vous obtenez un IOException ou d'une autre exception, le bloc de code « anormalement sortie » sans fermer la URLConnection. Cela se traduira par la fuite d'un descripteur de fichier. Pour ce faire, trop de fois et votre application échouera en raison de l'épuisement des descripteurs de fichiers ... ou les numéros de port locaux.

    Il est préférable d'utiliser un try / finally pour faire en sorte que URLConnections, prises, cours d'eau et ainsi de suite à ce lien vers le bas des ressources externes sont toujours fermés.


6 commentaires

Tout changement que vous m'avez recommandé dans l'application ou sur le côté serveur. N'importe quel code que j'ai besoin d'ajouter ici


Je prends vraiment l'exception, je n'ai pas simplement posté ce code ci-dessus, tout le code ci-dessus réside à l'intérieur d'essayer des déclarations de capture


Ce n'est pas seulement une question d'attraper l'exception. Vous devez fermer la connexion dans un bloc enfin aussi.


Bonjour, j'ai ajouté le code complet de la méthode que j'utilise dans la modification.


Oui. Je vois. Comme je m'attendais (et comme je l'ai expliqué ci-dessus), si une exception est levée entre l'ouverture de la connexion et la fermeture de l'introuvable, votre code est susceptible de fuir un descripteur de fichier de socket.


Lorsque le serveur utilise un codage de transfert chunté (commun pour les charges utiles volumineuses qui sont diffusées en continu), la longueur du contenu est inconnue et ne peut pas être dépendante. Il est très mauvais de tenter de pré-allouer un tampon pour toute la réponse, pouvez-vous dire déni de service ? Voir la réponse ci-dessous concernant le codage de transfert ...



0
votes

voir ici: http : ///download.oracle.com/javase/6/docs/api/java/net/urlconnection.html#getContElth%28%29

Il dit,

retours:

La longueur du contenu de la ressource que l'URL de cette connexion références, ou -1 si la longueur du contenu n'est pas connue.

Cela signifie simplement l'en-tête ne contient pas le champ longueur de contenu . Si vous avez le contrôle du code serveur. Vous devez définir le contenu-sous-traitant d'une manière utile. Quelque chose comme servleResponse # SetContentLength


1 commentaires

La longueur du contenu de la ressource que la présente URL se réfère, ou -1 si la longueur du contenu n'est pas connue.



0
votes

Je rencontre aussi ce problème dans mon application de papier peint. Le problème est que votre serveur ne fournit pas longueur de contenu dans son en-tête HTTP. Voici un instantané sur une en-tête HTTP normale avec longueur de contenu .

Entrez l'image Description Ici

J'utilise un hôte de partage afin que je ne puisse pas modifier la configuration du serveur. Dans mon application, je suis défini la valeur de dialogue de progression Max avec une valeur approximée (qui est plus grande que ma taille de fichier réelle) comme ceci: xxx

Vous pouvez également vérifier mon exemple complet code source ici:

Exemple de dialogue de progression Android .


0 commentaires

3
votes

Il semble que le serveur n'offre pas longueur de contenu dans ses en-têtes de réponse, avez-vous obtenu le codage de transfert = chunked en-tête des en-têtes de réponse?

Ma situation est la suivante: j'effectue une httpurlconnection et considérez que le serveur me répondrait à la "longueur de contenu" avec une valeur positive, mais je ne me suis pas tourné vers Le androidhttpclient quelle implémentation httpclient Android, réalise la même demande et obtenu la droite longueur de contenu .

J'ai utilisé Wireshark pour analyser les deux demandes, trouvé une petite différence sur les en-têtes de demande.

La liste d'en-tête qui utilise AndroidHTTPClient: xxx

la liste d'en-tête de demande qui utilise httpurlConnection: < Pré> xxx

La seule différence avec l'en-tête de la demande est Accepter-coding-coding qui n'est pas ajouté par moi-même, c'était un paramètre Android par défaut pour httpurlconnection , après cela, je l'ai défini sur identité puis exécutez la demande, ci-dessous Les piles d'en-tête complètes: xxx

Comme vous pouvez le constater, après avoir défini l'encodage accepté sur "Identity" Remplacer la valeur par défaut du système "Gzip", le serveur fourni "Contenu- Longueur "positif, c'est pourquoi AndroidhttpClient pourrait prendre la bonne valeur de la longueur de contenu et httpurlconnection non.

L'encodage de la compression GZIP peut provoquer une réponse chuntée qui considère par le côté serveur, et si le serveur pense que vous pouvez recevoir une réponse de codage chuntée, elle peut ne pas offrir le contenu -Length en-tête, essayez de désactiver le comportement acceptable Gzip puis voyez quelle différence avec celle-là.


1 commentaires

Soyez conseillé: Désactiver le codage de transfert peut entraîner une question de performance sur le serveur, car il ne peut plus «diffuser» tout ce que vous avez demandé, et doit «calculer» toute la réponse avant de l'envoyer. Le meilleur plan est de ne pas dépendre de cet en-tête et de retirer simplement. Ceci est indépendant de la compression, etc.



35
votes

Par défaut, cette implémentation de httpurlConnection demande que les serveurs utilisent la compression GZIP.
Depuis GetContentLength () renvoie le nombre d'octets transmis, vous ne pouvez pas utiliser cette méthode pour prédire le nombre d'octets pouvant être lus à partir de GetInputStream ().
Au lieu de cela, lisez ce flux jusqu'à ce qu'il soit épuisé: quand lire () retourne -1.
La compression GZIP peut être désactivée en définissant les codages acceptables dans l'en-tête de la demande: xxx

alors essayez ceci: xxx

source (paragraphe de performance) : http://developer.android.com/reference/java/net/httpurlconnection. HTML


2 commentaires

Merci, c'est une réponse parfaite :)


Wow! Cela a fonctionné. J'étais sur le point de jeter mon ordinateur portable: p merci un paquet