我正在使用TIdHTTPServer
使用ResponseInfo->ServeFile
功能向客户端提供文件。这适用于“静态”的文件:不是由其他进程编写的。从代码中我可以看出,ServeFile函数在内部使用TIdReadFileExclusiveStream
,它禁止我读取正在写入的文件,但我需要能够发送由其他进程写入的文件。
所以,我自己创建了一个FileStream并使用ContentStream
属性将其返回给客户端,但是我在客户端获得了一个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个字节。
答案 0 :(得分:2)
WriteHeader()
和WriteContent()
期望ContentStream
在被调用时完整且不变。如果WriteHeader()
属性为-1(您实际上是自己设置值),则Content-Length
使用当前ContentStream->Size
值创建AResponseInfo->ContentLength
标头,并WriteContent()
发送只有当前ContentStream->Size
值所示的字节数。因此,您的客户端正在接收0个字节,因为在您呼叫Size
和WriteHeader()
时文件WriteContent()
仍为0。
ServeFile()
和ContentStream
都不适合您的需求。由于文件是实时写入的,因此在创建HTTP标头并将其发送到客户端时,您不知道最终文件大小。因此,您必须使用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;
}
答案 1 :(得分: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在内部结束的工作)可以正常工作。