2013-02-27 6 views
7

私は専用のサーバーにHTTPマルチパートをアップロードするQNetworkAccessManagerを使用しようとしています。QNetworkAccessManager:シリアルのQIODeviceからのポストのHTTPマルチパート

マルチは、アップロードされたデータを記述するJSON部分から成ります。

データは、データを暗号化シリアルのQIODeviceから読み出されます。 p_encDeviceがqfileによりのインスタンスである場合、そのファイルは正常にアップロードされます、

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 

この

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

専門の暗号化のQIODeviceを使用する場合(シリアルデバイス)、データのすべてが私のカスタムデバイスから読み込まれます。 QNetworkAccessManager :: post()は完了(ハング)しません。

私がいることをQHttpPartのドキュメントでお読みください。)デバイスがシーケンシャルである場合、デバイスは(完成放出さ を持っていた後(例えば、ソケットではなくファイル)、 QNetworkAccessManagerは::ポスト()と呼ばれるべき

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

お知らせください。

EDIT:

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

EDIT 2:

QNAMが全くシーケンシャル・デバイスでは動作しないようです。 discussion on qt-projectを参照してください。

EDIT 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); 
} 
+0

適切な信号は 'QIODevice :: readChannelFinished()'だと思います。基本的には 'QIODevice :: bytesAvailable()'は正しい値を返さなければなりません。 –

+0

それ以来、問題は解決しましたか、matejkですか? – lpapp

+0

私はそれを解決することができましたが、きれいな方法では解決できませんでした。以下の私のコメントを参照してください。 – matejk

答えて

0

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

+0

どのようにこのユースケースを解決するには?私たちはメモリ内の大きなデータと妥協する必要がありますか、代わりにmmap'ingを開始しますか? – lpapp

+0

@LaszloPapp暗号化デバイスを非シーケンシャルと宣言するように暗号化デバイスを作成しましたが、read()およびseek()関数は現在の位置を検証してデータが順次読み込まれるようにします。 QNAMがデータを順番に読み取っています。それはうまくないですが、それは動作します。 – matejk

2

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

ほとんどの場合、リファクタリングして、QIODevice::finished()信号に接続できる新しいスロットを追加するだけです。

+0

ニコラス、ありがとう。これは、ポストが呼び出される前に、着信p_encDeviceからのすべてのデータがQNetworkAccessManagerの内部バッファに読み込まれることを意味しますか?もしそうなら、データをQByteArrayに読み込んでそれをQHttpPart :: setBodyに渡すとはるかに簡単です。 – matejk

+0

これはQNAMバッファに読み込まれませんが、今度はfilePartを読み込み続けるつもりですが、作成するスロットがアクセスできるようにfilePartをクラスメンバーにする必要があります。 –

+0

QIODeviceはスロットをまったく終了していません。さらに、QNetworkAccessManager :: postが呼び出されず、デバイスがそのようなイベントを発行できない場合、私のカスタムIODeviceからの読み取りはまったく起こりません。 – matejk

1

QHttpPartQHttpMultiPartを使用する場合よりもhttpポストデータを手動で作成する方が成功しました。私はそれがあなたが聞きたいと思うものではないと知っています。それはちょっと面倒ですが、間違いなく動作します。この例では、私はQFileから読んでいますが、QIODeviceにはreadAll()と呼ぶことができます。また、すべてのデータが読み込まれているかどうかを確認するのに役立つでしょう。QIODevice::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."; 
?> 

を。

+0

私は現在、完全なデータを読んで投稿していますが、ファイルが非常に大きい(数GB)ため、これを買う余裕がありません。 – matejk

+0

@matejk:正確には、readAllはreadAll以上のQNAMからの投稿は必要ありませんが、大きなファイルがある場合にはこれを行うことはできません。 – lpapp

1

QNetworkAccessManagerが(POST、PUT)データをアップロードするときにchunked transfer encodingをサポートしていないというキャッチだと思います。これは、QNAMがContent-Lengthヘッダーを送信するために、アップロードするデータの長さを事前に知っていなければならないことを意味します。これが意味:

  1. いずれかのデータありませんがシーケンシャル・デバイスから、しかし正しくsize()を通じて合計サイズを報告するランダム・アクセス・デバイス、から来ていません。
  2. このデータはシーケンシャルデバイスからのものですが、デバイスはすでにバッファリングしています(これは約finished()というメモの意味です)、それを報告します(これはbytesAvailable()です)。
  3. またはデータが順番に
    1. を意味いずれかQNAMが(EOFまで読むことによって)デバイスからすべてのデータを読み込み、バッファ自体のすべてのデータをバッファリングしていないシーケンシャルデバイス、から来ている
    2. かユーザーは要求のContent-Lengthヘッダーを手動で設定します。

(最後の二つの点について、QNetworkRequestのためのドキュメントを参照してください:: DoNotBufferUploadDataAttribute。)

ので、QHttpMultiPartは何とかこれらの制限を共有し、そしてそれがケースに3想定を窒息だと考えられますあなたの "エンコーダ" QIODeviceのすべてのデータをメモリにバッファリングすることはできません。事前にエンコードされたデータのサイズを知り、QHttpPartでコンテンツ長を設定する可能性はありますか?

(最後の注意として、QScopedPointerを使用しないでください。スマートポインタが有効範囲外になったときにQNRが削除されますが、そのようにしたくない場合はQNRを削除します。終了しました())。

+0

他のオプションをお試しいただきありがとうございます。私はDoNotBufferUploadDataAttributeを設定し、内容の長さを明示的に設定しています。なぜなら、事前にサイズを知っているからです。入力ストリームはEOFに読み込まれますが、すべてが停止します。 QIODeviceがまったく出力しないsignal finished()を記述しているドキュメントがあります。 – matejk

+0

次のコードはQNRが終了するのを待っているため、ScopedPointerを意図的に使用しています。 – matejk

+0

@matejk:はい、私は、Qtイベントループがそれを守っているので、QNetworkReplyがそれによって削除できないメイン関数でQScopedPointerも使用しています。ええ、残念ながらfinished()シグナルはありません。 – lpapp

関連する問題