在写入文件时使用 IdHTTPServer 提供文件
Serving files with IdHTTPServer when the files are being written
我正在与 TIdHTTPServer
合作,使用 ResponseInfo->ServeFile
函数向客户端提供文件。这适用于 "static": 未被其他进程写入的文件。据我从代码中可以看出,ServeFile 函数在内部使用 TIdReadFileExclusiveStream
,这不允许我读取正在写入的文件,但我还需要能够发送其他进程正在写入的文件.
所以,我自己创建了一个 FileStream,并使用 ContentStream
属性 到 return 到客户端,但我在客户端得到了一个 0 字节的文件(对于任何文件,无论是否正在写入),我看不到我遗漏了什么或做错了什么。这是我在 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 个字节。
WriteHeader()
和 WriteContent()
期望 ContentStream
在调用时完整且不变。如果 AResponseInfo->ContentLength
属性 为 -1,WriteHeader()
使用当前 ContentStream->Size
值创建一个 Content-Length
header (您实际上是自己设置该值) , 而 WriteContent()
只发送与当前 ContentStream->Size
值一样多的字节。因此,您的客户端收到 0 个字节,因为在您调用 WriteHeader()
和 WriteContent()
.
时文件 Size
仍为 0
ServeFile()
和 ContentStream
都不适合您的需要。由于文件是实时写入的,因此在创建 HTTP headers 并将其发送到客户端时,您不知道最终文件的大小。所以你必须使用HTTP 1.1的chunked
transfer coding来发送文件数据。这将允许您在写入文件时以块的形式发送文件数据,然后在文件完成时向客户端发送信号。
然而,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;
}
最终的工作代码是:
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 没有 return 任何内容到客户端,但是直接执行 IOHandler->Write
(这是 ServeFile 在内部结束时所做的)工作正常。
我正在与 TIdHTTPServer
合作,使用 ResponseInfo->ServeFile
函数向客户端提供文件。这适用于 "static": 未被其他进程写入的文件。据我从代码中可以看出,ServeFile 函数在内部使用 TIdReadFileExclusiveStream
,这不允许我读取正在写入的文件,但我还需要能够发送其他进程正在写入的文件.
所以,我自己创建了一个 FileStream,并使用 ContentStream
属性 到 return 到客户端,但我在客户端得到了一个 0 字节的文件(对于任何文件,无论是否正在写入),我看不到我遗漏了什么或做错了什么。这是我在 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 个字节。
WriteHeader()
和 WriteContent()
期望 ContentStream
在调用时完整且不变。如果 AResponseInfo->ContentLength
属性 为 -1,WriteHeader()
使用当前 ContentStream->Size
值创建一个 Content-Length
header (您实际上是自己设置该值) , 而 WriteContent()
只发送与当前 ContentStream->Size
值一样多的字节。因此,您的客户端收到 0 个字节,因为在您调用 WriteHeader()
和 WriteContent()
.
Size
仍为 0
ServeFile()
和 ContentStream
都不适合您的需要。由于文件是实时写入的,因此在创建 HTTP headers 并将其发送到客户端时,您不知道最终文件的大小。所以你必须使用HTTP 1.1的chunked
transfer coding来发送文件数据。这将允许您在写入文件时以块的形式发送文件数据,然后在文件完成时向客户端发送信号。
然而,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;
}
最终的工作代码是:
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 没有 return 任何内容到客户端,但是直接执行 IOHandler->Write
(这是 ServeFile 在内部结束时所做的)工作正常。