Pourquoi devez-vous appeler URLConnection#getInputStream pour être en mesure d'écrire dans URLConnection#getOutputStream?
Je suis en train d'écrire à URLConnection#getOutputStream
, cependant, aucune donnée n'est en fait envoyée jusqu'à ce que j'appelle URLConnection#getInputStream
. Même si j'ai mis URLConnnection#doInput
à false, il n'est toujours pas en envoyer. Personne ne sait pourquoi il en est? Il n'y a rien dans la documentation de l'API qui décrit cette.
Java Documentation de l'API sur URLConnection: http://download.oracle.com/javase/6/docs/api/java/net/URLConnection.html
De Java Tutoriel sur la Lecture et l'Écriture sur un URLConnection: http://download.oracle.com/javase/tutorial/networking/urls/readingWriting.html
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
public class UrlConnectionTest {
private static final String TEST_URL = "http://localhost:3000/test/hitme";
public static void main(String[] args) throws IOException {
URLConnection urlCon = null;
URL url = null;
OutputStreamWriter osw = null;
try {
url = new URL(TEST_URL);
urlCon = url.openConnection();
urlCon.setDoOutput(true);
urlCon.setRequestProperty("Content-Type", "text/plain");
////////////////////////////////////////
//SETTING THIS TO FALSE DOES NOTHING //
////////////////////////////////////////
//urlCon.setDoInput(false);
osw = new OutputStreamWriter(urlCon.getOutputStream());
osw.write("HELLO WORLD");
osw.flush();
/////////////////////////////////////////////////
//MUST CALL THIS OTHERWISE WILL NOT WRITE OUT //
/////////////////////////////////////////////////
urlCon.getInputStream();
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//If getInputStream is called while doInput=false, the following exception is thrown: //
//java.net.ProtocolException: Cannot read from URLConnection if doInput=false (call setDoInput(true)) //
/////////////////////////////////////////////////////////////////////////////////////////////////////////
} catch (Exception e) {
e.printStackTrace();
} finally {
if (osw != null) {
osw.close();
}
}
}
}
Vous devez vous connecter pour publier un commentaire.
L'API pour URLConnection et HttpURLConnection sont (pour le meilleur ou pour le pire) conçu pour l'utilisateur de suivre de très spécifique de la séquence des événements:
Si votre demande est un POST ou PUT, vous avez besoin de l'option de l'étape #2.
Au meilleur de ma connaissance, les OutputStream n'est pas comme une prise, il n'est pas directement connecté à un InputStream sur le serveur. Au lieu de cela, après la fermeture ou le rincer avec de l'eau ET appeler getInputStream(), la sortie s'est construite sur une Demande et a envoyé. La sémantique est basée sur l'hypothèse que vous aurez envie de lire la réponse. Tous les exemples que j'ai vu montre cet ordre d'événements. Je serais certainement d'accord avec vous et d'autres personnes que cette API est contre-intuitif quand par rapport à la normale de flux d'e/S de l'API.
La tutoriel - vous un lien vers les états que "URLConnection est un HTTP centrée sur la classe". J'interprète cela comme voulant dire que les méthodes sont conçues autour d'un modèle Requête-Réponse, et de faire l'hypothèse qui est la façon dont ils seront utilisés.
Pour ce que ça vaut, j'ai trouvé ce rapport de bug qui explique le fonctionnement prévu de la classe mieux que la javadoc de la documentation. L'évaluation du rapport stipule que: "La seule façon d'envoyer la demande en appelant getInputStream."
Bien que le getInputStream() la méthode peut certainement provoquer une URLConnection objet de lancer une requête HTTP, il n'est pas une obligation de le faire.
Compte du flux de travail:
L'étape 1 comprend la possibilité d'inclure des données dans la demande, par voie de un HTTP entité. Il se trouve que la classe URLConnection fournit un OutputStream objet en tant que mécanisme pour la fourniture de ces données (et à juste titre, pour de nombreuses raisons qui ne sont pas particulièrement pertinente ici). Il suffit de dire que le flux de la nature de ce mécanisme fournit au programmeur un montant de flexibilité lors de la fourniture des données, y compris la possibilité de fermer le flux de sortie (et de tous les flux d'entrée d'alimentation il), avant de terminer la demande.
En d'autres termes, l'étape 1 permet la fourniture d'une entité de données pour la demande, de continuer à construire (par exemple en ajoutant des en-têtes).
L'étape 2 est vraiment un pas virtuel, et peut être automatisé (comme il est dans la classe URLConnection), depuis le dépôt d'une demande est vide de sens sans une réponse (au moins dans les limites du protocole HTTP).
Qui nous amène à l'Étape 3. Lors du traitement d'une réponse HTTP, la réponse de l'entité -- extrait en appelant getInputSteam() -- est juste l'une des choses que nous pourrions être intéressés. Une réponse consiste en un état, en-têtes, et éventuellement une entité. La première fois que l'un de ces est demandé, la URLConnection effectuera virtuel de l'étape 2 et de soumettre la demande.
Peu importe si une entité est envoyé par l'intermédiaire de la connexion flux de sortie ou pas, et peu importe si une entité de réponse est de retour prévue, un programme TOUJOURS envie de savoir la suite (comme prévu par le code de statut HTTP). L'appel de getResponseCode() sur le URLConnection offre ce statut, et de la commutation sur le résultat peut mettre fin au HTTP conversation, sans jamais suivre getInputStream().
Donc, si les données sont soumises, et une entité de réponse n'est pas prévu, ne pas faire ceci:
... ce faire:
Que mes expériences ont montré (java 1.7.0_01) le code:
Ne pas envoyer quoi que ce soit sur le serveur. Il sauve juste ce qui est écrit là à la mémoire tampon. Ainsi dans le cas où vous allez télécharger un fichier volumineux par la POSTE - vous avez besoin pour vous assurer que vous avez suffisamment de mémoire. Sur desktop/serveur, il peut ne pas être un gros problème, mais sur android qui peut entraîner des erreurs de mémoire insuffisante. Voici l'exemple de la façon dont la trace de la pile ressemble quand lors de l'écriture du flux de sortie, et plus de mémoire.
Sur le bas de la trace, vous pouvez voir le
makePOST()
méthode qui effectue les opérations suivantes:Et
writer.write()
lève l'exception.Aussi mes expériences ont montré que toute exception liées à la connexion des entrées/sorties avec le serveur est lancé qu'après
urlCon.getOutputStream()
est appelé. MêmeurlCon.connect()
semble être "factice" méthode qui ne fait pas de connexion physique.Toutefois, si vous appelez
urlCon.getContentLengthLong()
qui renvoie Content-Length: champ d'en-tête de la réponse du serveur-headers - alors URLConnection.getOutputStream() sera appelée automatiquement et en cas d'exception, il sera jeté.Les exceptions levées par
urlCon.getOutputStream()
sont tous IOException, et j'ai rencontré les suivantes: lesJ'espère que mon peu de recherche en aide à des personnes en tant que classe URLConnection est un peu contre-intuitif, dans certains cas, donc, lors de la mise en œuvre, il a besoin de savoir ce qu'il traite.
Deuxième raison est la suivante: lorsque l'on travaille avec des serveurs - le travail avec server peut échouer pour de nombreuses raisons (connexion, dns, pare-feu, httpresponses, le serveur n'étant pas en mesure d'accepter la connexion, le serveur n'étant pas en mesure de traiter la demande dans les délais). Ainsi, il est important de comprendre comment les exceptions soulevées peuvent expliquer à propos de ce qui se passe réellement avec la connexion.
Appel getInputStream() indique que le client est la fin de l'envoi, il est demande, et il est prêt à recevoir la réponse (par HTTP spec). Il semble que la classe URLConnection a cette notion construit en elle, et doit être flush()ing le flux de sortie lorsque le flux d'entrée est demandé.
Que l'autre intervenant a noté, vous devriez être en mesure d'appeler la méthode flush() vous-même pour déclencher l'écriture.
flush()
, vous verrez que je l'ai essayé et je ne le vois pas demande de venir en parcourant sur l'autre extrémité.getOuputStream()
,flush()
,write()
, et enfinflush()
de nouveau?La raison fondamentale est qu'il a pour le calcul d'un Contenu de l'en-tête de longueur automatiquement (sauf si vous utilisez mémorisé en bloc ou en mode streaming). Il ne peut pas le faire jusqu'à ce qu'il a vu de toutes les données de sortie, et il a envoyer avant la sortie, de sorte qu'il a pour le tampon de sortie. Et il a besoin d'un événement décisif de savoir quand la dernière sortie a été effectivement écrit. Elle utilise donc getInputStream() pour cela. A cette époque, il écrit les en-têtes, y compris la longueur du contenu, puis la sortie, puis il commence la lecture de l'entrée.
(Repost de votre première question. Sans vergogne auto-plug)
Ne pas bricoler avec URLConnection vous-même, laissez Resty gérer.
Voici le code, vous devez écrire (je suppose que vous êtes l'obtention de texte):