7

QNetworkAccessManager を使用して http マルチパートを専用サーバーにアップロードしようとしています。

マルチパートは、アップロードされるデータを記述する JSON パートで構成されます。

データは、データを暗号化するシリアル QIODevice から読み取られます。

これは、マルチパート リクエストを作成するコードです。

QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

QHttpPart metaPart;
metaPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
metaPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"metadata\""));
metaPart.setBody(meta.toJson());
multiPart->append(metaPart);

QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(fileFormat));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\""));
filePart.setBodyDevice(p_encDevice);
p_encDevice->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(filePart);

QNetworkAccessManager netMgr;
QScopedPointer<QNetworkReply> reply( netMgr.post(request, multiPart) );
multiPart->setParent(reply.data()); // delete the multiPart with the reply

p_encDevice が QFile のインスタンスである場合、そのファイルは問題なくアップロードされます。

特殊な暗号化 QIODevice (シリアル デバイス) が使用されている場合、すべてのデータがカスタム デバイスから読み取られます。ただし、QNetworkAccessManager::post() は完了しません (ハングします)。

QHttpPartのドキュメントを読みました:

デバイスがシーケンシャルな場合 (たとえば、ファイルではなくソケット)、QNetworkAccessManager::post() は、デバイスが finished() を発行した後に呼び出す必要があります。

残念ながら、私はそれを行う方法がわかりません。

お知らせ下さい。

編集:

QIODevice には finished() スロットがまったくありません。さらに、QNetworkAccessManager::post() が呼び出されない場合、カスタム IODevice からの読み取りはまったく行われないため、デバイスはそのようなイベントを発行できません。(キャッチ22?)

編集2:

QNAM はシーケンシャル デバイスではまったく機能しないようです。qt-project の議論を参照してください。

編集3:

私はなんとか QNAM を「騙して」、ノンシーケンシャル デバイスから読み取っていると思わせましたが、シークおよびリセット機能によってシークが妨げられました。これは、QNAM が実際にシークを試みるまで機能します。

bool AesDevice::isSequential() const
{
    return false;
}

bool AesDevice::reset()
{
    if (this->pos() != 0) {
        return false;
    }
    return QIODevice::reset();
}

bool AesDevice::seek(qint64 pos)
{
    if (this->pos() != pos) {
        return false;
    }
    return QIODevice::seek(pos);
}
4

4 に答える 4

2

post渡す変数が投稿した関数の外部で使用できるように、コードをかなりリファクタリングする必要があります。その後post、実装内で実行するためのコードで定義された新しいスロットが必要になります。connect(p_encDevice, SIGNAL(finished()), this, SLOT(yourSlot())最後に、すべてを接着する必要があります。

ほとんどの場合、それをリファクタリングして、QIODevice::finished()信号に関連付けることができる新しいスロットを追加する必要があります。

于 2013-02-27T13:45:27.897 に答える
1

キャッチは、QNetworkAccessManagerが(POST、PUT)データをアップロードするときにチャンク転送エンコーディングをサポートしていないことだと思います。つまり、QNAMは、Content-Lengthヘッダーを送信するために、アップロードするデータの長さを事前に知っている必要があります。これは次のことを意味します。

  1. データはシーケンシャルデバイスからではなく、ランダムアクセスデバイスからのものであり、size();を介してそれらの合計サイズを正しく報告します。
  2. または、データはシーケンシャルデバイスから取得されますが、デバイスはすでにすべてのデータをバッファリングしており(これはについてのメモの意味ですfinished())、レポートします(を介してbytesAvailable()、私は推測します)。
  3. または、データがすべてのデータをバッファリングしていないシーケンシャルデバイスからのものであるため、
    1. QNAMは、デバイスからのすべてのデータを読み取り、バッファリングします(EOFまで読み取ることにより)
    2. または、ユーザーがリクエストのContent-Lengthヘッダーを手動で設定します。

(最後の2つのポイントについては、QNetworkRequest :: DoNotBufferUploadDataAttributeのドキュメントを参照してください。)

したがって、QHttpMultiPartはどういうわけかこれらの制限を共有し、ケース3で窒息している可能性があります。「エンコーダ」QIODeviceからのすべてのデータをメモリにバッファリングできないと仮定すると、エンコードされたデータのサイズを知っている可能性があります。 QHttpPartでcontent-lengthを進めて設定しますか?

(最後の注意として、QScopedPointerを使用しないでください。スマートポインターがスコープから外れるとQNRが削除されますが、その必要はありません。finished()を出力するときにQNRを削除します。 )。

于 2013-03-09T12:46:55.683 に答える
1

QHttpPartandを使用するよりも、httppostデータを手動で作成する方が成功していますQHttpMultiPart。おそらくあなたが聞きたいものではないことを私は知っています、そしてそれは少し厄介ですが、それは間違いなく機能します。この例では、から読んでいますが、任意のQFileを呼び出すことができます。また、すべてのデータが読み取られたかどうかを確認するのに役立ちます。readAll()QIODeviceQIODevice::size()

QByteArray postData;
QFile *file=new QFile("/tmp/image.jpg");
if(!(file->open(QIODevice::ReadOnly))){
    qDebug() << "Could not open file for reading: "<< file->fileName();
    return;
}
//create a header that the server can recognize
postData.insert(0,"--AaB03x\r\nContent-Disposition: form-data; name=\"attachment\"; filename=\"image.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n");
postData.append(file->readAll());
postData.append("\r\n--AaB03x--\r\n");
//here you can add additional parameters that your server may need to parse the data at the end of the url
QString check(QString(POST_URL)+"?fn="+fn+"&md="+md);
QNetworkRequest req(QUrl(check.toLocal8Bit()));
req.setHeader(QNetworkRequest::ContentTypeHeader,"multipart/form-data; boundary=AaB03x");
QVariant l=postData.length();
req.setHeader(QNetworkRequest::ContentLengthHeader,l.toString());
file->close();
//free up memory
delete(file);
//post the data
reply=manager->post(req,postData);
//connect the reply object so we can track the progress of the upload        
connect(reply,SIGNAL(uploadProgress(qint64,qint64)),this,SLOT(updateProgress(qint64,qint64)));

次に、サーバーは次のようにデータにアクセスできます。

<?php
$filename=$_REQUEST['fn'];
$makedir=$_REQUEST['md'];
if($_FILES["attachment"]["type"]=="image/jpeg"){
if(!move_uploaded_file($_FILES["attachment"]["tmp_name"], "/directory/" . $filename)){
    echo "File Error";
    error_log("Uploaded File Error");
    exit();
};
}else{
print("no file");
error_log("No File");
exit();
}
echo "Success.";
?>

このコードのいくつかがお役に立てば幸いです。

于 2013-03-08T15:26:03.193 に答える
0

qt-projectでの別の議論から、ソース コードを調べると、QNAM はシーケンシャルではまったく機能しないようです。ドキュメントとコードの両方が間違っています。

于 2013-03-13T11:16:01.167 に答える