【问题标题】:Resume HTTP Post/upload with Indy使用 Indy 恢复 HTTP 发布/上传
【发布时间】:2012-03-24 19:00:00
【问题描述】:

我正在尝试使用 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 中,因为这里有无限超时:

TIdSocketListWindows.FDSelect()

【问题讨论】:

  • 你应该使用PUT
  • 这是remy lebeau said about put
  • 不知道,他可能在谈论PHP4。但是,是的,您的愿望与 POST 不相容。
  • 恢复文件与PUT 不兼容。我说了很多,另一方面,POST 只是任意数据。接收脚本决定如何处理数据,因此理论上它可以用于恢复。在实践中,它很少。

标签: delphi post upload indy resume


【解决方案1】:

正如我在your earlier question 中告诉你的,你必须发布一个TStream,其中包含要上传的剩余文件数据。不要使用TIdMultipartFormDataStream.AddFile(),因为这会发送整个文件。请改用TStreamTIdMultipartFormDataStream.AddFormField() 重载版本。

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 值在错误的位置。您将它们应用于整个Request,这是错误的。它们需要在FormField 上应用,但TIdMultipartFormDataStream 目前不支持。其次,multipart/form-data 无论如何都不是为了这样使用而设计的。从头开始上传文件很好,但不能恢复损坏的上传。你真的应该停止使用TIdMultipartFormDataStream,直接将文件数据传递给TIdHTTP.Post(),就像我suggested earlier一样,例如:

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;

我已经explained earlier 如何在 PHP 中访问原始的POST 数据。

【讨论】:

  • 很抱歉,您的上一篇文章中并不清楚。另外,我使用 TIdMultipartFormDataStream 的原因是它可以很好地处理大文件。此解决方案意味着必须拆分/复制文件(如果文件很大,这会减慢处理速度)并且只需 .
  • 感谢您的额外帮助,我认为更新 indy 以支持恢复上传并不是一个坏主意,但您比我更了解 indy/http。
  • 正如我之前解释的,您不需要也不应该拆分文件。发布TIdMultipartFormDataStream 与普通的TStream 相比,您正在尝试做的事情太过分了。发布一个普通的TStream 处理大文件的效果与发布一个TIdMultipartFormDataStream 一样好(如果不是更好的话),而无需处理所有额外的开销。正如我之前解释的那样,HTTP 本身不支持上传恢复,因此您使用的任何解决方案都将是一个 hack,因为您必须手动编写后端 PHP 脚本来支持它。
  • Remy,You said in the other question 说“您可以选择编写单独的脚本 (...) 和另一个脚本,以便在恢复之前的上传时将剩余数据 POST 到。”所以(在 ​​Indy 级别上说话,而不是 php)我确实需要拆分文件(不仅仅是设置 Stream.Position),否则 Indy 将完整发送文件(这就是我在至少)。
  • 我明白 PHP 脚本会是 hackish,但我不明白如何只发送部分流而不先拆分它(因为 Indy 不会继续上传并根据我的测试,无论 Stream.Position 值如何,它都会发送完整文件)
猜你喜欢
  • 2010-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-24
  • 1970-01-01
  • 2013-12-16
  • 1970-01-01
相关资源
最近更新 更多