Téléchargement d’un fichier à l’aide de la méthode post () de QNetworkAccessManager

J’ai des problèmes avec une application Qt; spécifiquement avec la classe QNetworkAccessManager. Je tente d’effectuer un téléchargement HTTP simple d’un fichier binary à l’aide de la méthode post () du QNetworkAccessManager. La documentation indique que je peux donner un pointeur à un QIODevice à post (), et que la classe transmettra les données trouvées dans le QIODevice. Cela me suggère que je devrais pouvoir donner à post () un pointeur sur un QFile. Par exemple:

QFile compressedFile("temp"); compressedFile.open(QIODevice::ReadOnly); netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload") ), &compressedFile); 

Ce qui semble se passer sur le système Windows où je développe ceci est que mon application Qt pousse les données du fichier QFile, mais ne termine pas la requête; il semble être assis là à attendre plus de données à afficher du fichier. La demande de publication n’est pas “fermée” jusqu’à ce que je tue manuellement l’application, à quel point tout le fichier apparaît à la fin du serveur.

À partir de certains travaux de débogage et de recherche, je pense que cela se produit car l’opération read () de QFile ne renvoie pas -1 lorsque vous atteignez la fin du fichier. Je pense que QNetworkAccessManager essaie de lire à partir de QIODevice jusqu’à ce qu’il obtienne un -1 de read (), à quel point il suppose qu’il n’y a plus de données et ferme la demande. S’il continue à obtenir un code de retour de zéro à partir de read (), QNetworkAccessManager suppose qu’il pourrait y avoir plus de données à venir, et il attend donc toujours ces données hypothétiques.

J’ai confirmé avec un code de test que l’opération read () de QFile retourne juste zéro après avoir lu à la fin du fichier. Cela semble être incompatible avec la façon dont la méthode post () de QNetworkAccessManager s’attend à ce qu’un QIODevice se comporte. Mes questions sont:

  1. Est-ce une sorte de limitation avec la façon dont QFile fonctionne sous Windows?
  2. Y a-t-il une autre manière d’utiliser QFile ou QNetworkAccessManager pour envoyer un fichier via post ()?
  3. Est-ce que cela ne fonctionnera pas du tout, et devrais-je trouver un autre moyen de télécharger mon fichier?

Toute suggestion ou suggestion serait appréciée.

Mise à jour: Il s’avère que j’avais deux problèmes différents: un côté client et un côté serveur. Du côté du client, je devais m’assurer que mon object QFile restait pendant la durée de la transaction réseau. La méthode post () de QNetworkAccessManager retourne immédiatement mais n’est pas terminée immédiatement. Vous devez attacher un slot au signal finish () de QNetworkAccessManager pour déterminer quand le POST est réellement terminé. Dans mon cas, il était assez facile de garder le QFile plus ou moins en permanence, mais j’ai également attaché un slot au signal terminé () afin de vérifier les réponses d’erreur du serveur.

J’ai attaché le signal à la fente comme ceci:

 connect(&netManager, SIGNAL(finished(QNetworkReply*) ), this, SLOT(postFinished(QNetworkReply*) ) ); 

Quand il était temps d’envoyer mon fichier, j’ai écrit le code postal comme ceci (notez que le fichier compressé est un membre de ma classe et ne sort donc pas de ce code):

 compressedFile.open(QIODevice::ReadOnly); netManager.post(QNetworkRequest(QUrl(httpDestination.getCSsortingng() ) ), &compressedFile); 

Le signal terminé (QNetworkReply *) de QNetworkAccessManager déclenche ma méthode postFinished (QNetworkReply *). Lorsque cela se produit, il est prudent de fermer le fichier compressé et de supprimer le fichier de données représenté par le fichier compressé. À des fins de débogage, j’ai également ajouté quelques instructions printf () pour confirmer que la transaction est terminée:

 void CL_QtLogCompressor::postFinished(QNetworkReply* reply) { QByteArray response = reply->readAll(); printf("response: %s\n", response.data() ); printf("reply error %d\n", reply->error() ); reply->deleteLater(); compressedFile.close(); compressedFile.remove(); } 

Le fichier compressé n’étant pas fermé immédiatement et n’étant pas hors de scope, le QNetworkAccessManager peut prendre autant de temps qu’il le souhaite pour transmettre mon fichier. Finalement, la transaction est terminée et ma méthode postFinished () est appelée.

Mon autre problème (qui a également consortingbué au comportement que je voyais où la transaction n’a jamais été terminée) était que le code Python de mon serveur Web ne contenait pas le POST correctement, mais cela ne relevait pas de la question Qt initiale.

Vous créez un fichier compressedFile sur la stack et vous passez un pointeur vers celui-ci sur votre QNetworkRequest (et finalement sur votre QNetworkAccessManager). Dès que vous quittez la méthode dans laquelle vous vous trouvez, compressedFilecompressedFile est hors de scope. Je suis surpris qu’il ne se bloque pas sur vous, même si le comportement n’est pas défini.

Vous devez créer le QFile sur le tas:

 QFile *compressedFile = new QFile("temp"); 

Vous devrez bien sûr en garder la trace, puis la delete une fois la publication terminée ou la définir comme l’enfant du QNetworkReply pour qu’elle soit détruite lorsque la réponse sera détruite plus tard:

 QFile *compressedFile = new QFile("temp"); compressedFile->open(QIODevice::ReadOnly); QNetworkReply *reply = netManager.post(QNetworkRequest(QUrl("http://mywebsite.com/upload") ), compressedFile); compressedFile->setParent(reply); 

Vous pouvez également planifier la suppression automatique d’un fichier alloué au tas à l’aide de signaux / logements.

 QFile* compressedFile = new QFile(...); QNetworkReply* reply = Manager.post(...); // This is where the sortingcks is connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()); connect(reply, SIGNAL(destroyed()), compressedFile, SLOT(deleteLater()); 

IMHO, il est beaucoup plus localisé et encapsulé que d’avoir à garder autour de votre fichier dans la classe externe.

Notez que vous devez supprimer le premier connect() si vous avez votre postFinished(QNetworkReply*) , dans lequel vous devez alors oublier d’appeler reply->deleteLater() intérieur de celui-ci pour que cela fonctionne.