使用Indy进行HTTP Post/上传简历

3

我正在尝试使用indy(HTTP Post)恢复上传,代码如下(使用Delphi 2010,Indy 10.4736):

 IdHttp.Head('http://localhost/_tests/resume/large-file.bin');
 ByteRange           := IdHttp.Response.ContentLength + 1;

 // Attach the file to post/upload
 Stream              := TIdMultipartFormDataStream.Create;
 with Stream.AddFile('upload_file', 'D:\large-file.bin', 'application/octet-stream') do
 begin
      HeaderCharset  := 'utf-8';
      HeaderEncoding := '8';
 end;    // with

 with IdHTTP do
 begin
      IOHandler.LargeStream           := True;

      with Request do
      begin
           ContentRangeStart          := ByteRange;
           ContentRangeEnd            := (Stream.Size - ByteRange);
           ContentLength              := ContentRangeEnd;
           ContentRangeInstanceLength := ContentLength;
      end;    // with

      Post('http://localhost/_tests/resume/t1.php', Stream);
 end;    // with

但上传简历不起作用 :(
我查看了Indy的代码,似乎在IdIOHandler.pas中的这个函数:
TIdIOHandler.Write()
总是处理完整的流/文件(因为参数ASize:TIdStreamSize似乎总是为0,根据代码意味着发送完整的文件/流)。
这会阻止indy恢复上传。
我的问题是:有没有可能避免发送完整的文件?
设置内容范围没有改变任何东西。我还调整了indy的代码(修改了3行),使indy遵守内容范围/流位置,但它有错误,并且indy最终始终挂起在IdStackWindows.pas中,因为无限超时。

你应该使用 PUT - OnTheFly
这是Remy Lebeau关于Put的评论。 - TheDude
不知道,他可能在谈论PHP4。但是是的,你的需求与POST不兼容。 - OnTheFly
恢复文件与“PUT”不兼容。我说过了,“POST”只是任意数据。接收脚本决定如何处理数据,因此理论上它可以用于恢复。但实际上很少这样做。 - Remy Lebeau
1个回答

4

正如我在你之前的问题中告诉过你的那样,你需要发布一个包含剩余文件数据的TStream以进行上传。不要使用TIdMultipartFormDataStream.AddFile(),因为那会发送整个文件。相反,请使用TIdMultipartFormDataStream.AddFormField()TStream重载版本。

TIdHTTP并不设计支持ContentRange...属性。大多数Request属性仅设置相应的HTTP头,它们并不影响数据。这就是为什么你的编辑破坏了它的原因。

试试这个:

IdHttp.Head('http://localhost/_tests/resume/large-file.bin');
FileSize := IdHttp.Response.ContentLength;

FileStrm := TFileStream.Create('D:\large-file.bin', fmOpenRead or fmShareDenyWrite);
try
  if FileSize < FileStrm.Size then
  begin
    FileStrm.Position := FileSize;

    Stream := TIdMultipartFormDataStream.Create;
    try
      with Stream.AddFormField('upload_file', 'application/octet-stream', '', FileStrm, 'large-file.bin') do
      begin
        HeaderCharset  := 'utf-8';
        HeaderEncoding := '8';
      end;

      with IdHTTP do
      begin
        with Request do
        begin
          ContentRangeStart := FileSize + 1;
          ContentRangeEnd   := FileStrm.Size;
        end;

        Post('http://localhost/_tests/resume/t1.php', Stream);
      end;
    finally
      Stream.Free;
    end;
  end;
finally
  FileStrm.Free;
end;

话虽如此,这是对 HTTP 和 multipart/form-data 的滥用。首先,ContentRange 值放错了地方。你把它们应用到整个请求中,这是错误的。它们需要应用在 FormField 上,但 TIdMultipartFormDataStream 目前不支持这样做。其次,multipart/form-data 并不是设计用来这样使用的。它适用于从头上传文件,但不适用于恢复中断的上传。你真的应该停止使用 TIdMultipartFormDataStream,直接像我之前建议的那样将文件数据直接传递给 TIdHTTP.Post()。

FileStrm := TFileStream.Create('D:\large-file.bin', fmOpenRead or fmShareDenyWrite);
try
  IdHTTP.Post('http://localhost/_tests/upload.php?name=large-file.bin', FileStrm);
finally
  FileStrm.Free;
end;

.

IdHTTP.Head('http://localhost/_tests/files/large-file.bin');
FileSize := IdHTTP.Response.ContentLength;

FileStrm := TFileStream.Create('D:\large-file.bin', fmOpenRead or fmShareDenyWrite);
try
  if FileSize < FileStrm.Size then
  begin
    FileStrm.Position := FileSize;
    IdHTTP.Post('http://localhost/_tests/resume.php?name=large-file.bin', FileStrm);
  end;
finally
  FileStrm.Free;
end;

我之前已经解释过如何在PHP中访问原始的POST数据。


抱歉,您上一篇文章的表述不够清晰。此外,我使用TIdMultipartFormDataStream的原因是它可以很好地处理大文件。这种解决方案意味着需要拆分/复制文件(如果文件过大,这可能会减慢进程),而且只需…… - TheDude
2
正如我之前所解释的,你不需要也不应该分割文件。使用TIdMultipartFormDataStream而不是普通的TStream是完全过度的,与你尝试做的事情相比。使用普通的TStream处理大文件的效果与使用TIdMultipartFormDataStream相同,甚至更好,而且不需要处理所有额外的开销。并且正如我之前所解释的,HTTP本身不支持上传恢复,因此无论你使用什么解决方案都将是一个hack,因为你必须手动编写后端PHP脚本来支持它。 - Remy Lebeau
我明白PHP脚本可能会有些“hackish”,但我不明白如何在不先拆分流的情况下只发送部分流(因为Indy不支持恢复上传,根据我的测试,它将发送完整文件而不考虑Stream.Position值)。 - TheDude
那么我应该基于TIdEventStream创建一个新的流类,并覆盖read/write/seek/SetSize例程(或使用相关事件)来处理发送部分内容而不更改Indy源代码,然后覆盖AddFormField(),我理解你的意思了吗? - TheDude
谢谢你,Remy!顺便说一下,我要向你道歉,因为我昨天才在聊天记录中发现了你的回复(其实我是 SO 的新手,而且我只是在谷歌上搜索时才发现它!) - TheDude
显示剩余23条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接