0

TIdHTTPServer関数を使用して、クライアントにファイルを提供するために作業していResponseInfo->ServeFileます。これは、「静的」なファイル、つまり他のプロセスによって書き込まれていないファイルに対してはうまく機能します。コードからわかる限り、ServeFile 関数は内部的に を使用しているため、書き込みTIdReadFileExclusiveStream中のファイルを読み取ることができませんが、他のプロセスによって書き込まれているファイルも送信できる必要があります。

そのため、私は自分で FileStream を作成し、ContentStreamプロパティを使用してそれをクライアントに返すようにしましたが、クライアントで 0 バイトのファイルを取得し (ファイルが書き込まれているかどうかに関係なく)、何が表示されているかわかりません。 m 行方不明または間違っています。OnCommandGetイベントハンドラーで使用しているコードは次のとおりです。

AResponseInfo->ContentStream = new TFileStream(path, fmOpenRead | fmShareDenyNone);
AResponseInfo->ContentStream->Position = 0;
AResponseInfo->ContentLength = AResponseInfo->ContentStream->Size;
AResponseInfo->ResponseNo = 200;
AResponseInfo->WriteHeader();
AResponseInfo->WriteContent();

この時点での ContentLength プロパティには有効な値 (つまり、ContentStream->Size を呼び出したときのファイル サイズ) があり、ファイルが途中で変更されたとしても、それをクライアントに送信したいと考えています。

WriteContent() 関数、WriteHeader() を削除しようとしましたが、結果は同じです。いくつかの例を検索しましたが、見つかったいくつかはこのコードとほぼ同じであるため、何が問題なのかわかりません。ほとんどの例には WriteContent() 呼び出しが含まれていないため、それらを削除しようとしましたが、違いはないようです。

補足として、書き込み中のファイルは書き込みが完了するまでに 24 時間かかりますが、それはクライアント側から予想されることです。必要なのは、要求時に既に書き込まれているバイトだけです (多少少なくても有効です)。ファイルが削除されることはありません。ファイルは大きくなり続けるだけです。

何か案は?

アップデート

Fiddler を使用すると、これに関連するプロトコル違反に関する警告が表示されます。たとえば、次のようになります。

Content-Length mismatch: Response Header indicated 111,628,288 bytes, but server sent 41 bytes

コンテンツの長さは正しく、それはファイル サイズですが、アプリが 41 バイトしか送信しなかった理由がわかりません。

4

2 に答える 2

2

WriteHeader()が呼び出された時点で完全で不変であること をWriteContent()期待します。プロパティが -1の場合 (実際には自分で値を設定している場合)、現在の値を使用してヘッダーを作成し、現在の値と同じ数のバイトのみを送信します。したがって、クライアントは 0 バイトを受信して​​います。これは、呼び出し時にファイルがまだ 0 であるためです。ContentStreamWriteHeader()Content-LengthContentStream->SizeAResponseInfo->ContentLengthWriteContent()ContentStream->SizeSizeWriteHeader()WriteContent()

どちらServeFile()ContentStreamあなたのニーズには適していません。ファイルはライブで書き込まれるため、HTTP ヘッダーが作成されてクライアントに送信されるときの最終的なファイル サイズはわかりません。そのため、 HTTP 1.1 の転送コーディングを使用してファイル データを送信する必要があります。これにより、ファイルが書き込まれているときにファイルデータをチャンクで送信し、ファイルが終了したときにクライアントに通知できます。chunked

ただし、応答のTIdHTTPServer送信をネイティブにサポートしていないため、手動で実装する必要があります。たとえば、次のようになります。chunked

TFileStream *fs = new TFileStream(path, fmOpenRead | fmShareDenyNone);
try
{
    AResponseInfo->ResponseNo = 200;
    AResponseInfo->TransferEncoding = "chunked";
    AResponseInfo->WriteHeader();

    TIdBytes buffer;
    buffer.Length = 1024;

    do
    {
        int NumRead = fs->Read(&buffer[0], 1024);
        if (NumRead == -1) RaiseLastOSError();
        if (NumRead == 0)
        {
            // check for EOF, unless you have another way to detect it...
            Sleep(1000);
            NumRead = fs->Read(&buffer[0], 1024);
            if (NumRead <= 0) break;
        }

        // send the current chunk
        AContext->Connection->IOHandler->WriteLn(IntToHex(NumRead));
        AContext->Connection->IOHandler->Write(buffer, NumRead);
        AContext->Connection->IOHandler->WriteLn();
    }
    while (true);

    // send the last chunk to signal EOF
    AContext->Connection->IOHandler->WriteLn("0");

    // send any trailer headers you need, if any...

    // finish the transfer encoding
    AContext->Connection->IOHandler->WriteLn();
}
__finally
{
    delete fs;
}
于 2015-02-09T20:42:07.640 に答える
0

最終的な作業コードは次のとおりです。

    std::unique_ptr< TFileStream >fs(new TFileStream(path, fmOpenRead | fmShareDenyNone));

    fs->Position = 0;
    __int64 size = fs->Size;

    AResponseInfo->ContentLength = size;
    AResponseInfo->ResponseNo    = 200;

    AResponseInfo->WriteHeader();

    AContext->Connection->IOHandler->Write(fs.get(), size);

sizeこれにより、ファイルが同時に書き込まれている場合でも、クライアントは元のファイルの最大バイト数を受け取ることができます。

何らかの理由で、ContentStream を渡してもクライアントにコンテンツが返されませんでしたが、IOHandler->Write直接実行すると (これは、ServeFile が内部で終了することです) 正常に動作します。

于 2015-02-10T17:30:42.683 に答える