Delphi XE2 DataSnap - 通过带有进度条的TStream下载文件

9
我已经编写了一个DataSnap服务器方法,用于返回TStream对象以传输文件。客户端应用程序调用该方法并成功读取流。问题在于,在TStream对象可读之前,方法调用需要较长时间,但在服务器端,我可以看到该方法调用仅需一秒钟即可创建要返回的对象。我希望流对象能够立即返回,以便我可以读取流并显示下载进度条。是否有其他方法可以做到这一点?
该服务器方法非常简单:
function TServerMethods.DespatchDocument(sCompanyID, sDocOurRef: string): TStream;
var
  sSourceFilePath: string;
  strFileStream: TFileStream;
begin
  sSourceFilePath := GetDocumentPDFFilePath(sCompanyID, sDocOurRef);

  strFileStream := TFileStream.Create(sSourceFilePath, fmOpenRead);
  Result := strFileStream;
end;

可能是 ProgressBar for TResourceStream (Delphi) 的重复问题。 - NGLN
2个回答

3
这是我一段时间以前的做法。我使用了XE,但还没有机会整理它。
//服务器端:
function TServerMethods1.DownloadFile(out Size: Int64): TStream;
begin
    Result := TFileStream.Create('upload.fil', fmOpenRead or fmShareDenyNone);
    Size := Result.Size;

    Result.Position := 0;
end;

//客户端:

procedure TfMain.DownloadFile(Sender: TObject);
var
    RetStream: TStream;
    Buffer: PByte;
    Mem: TMemoryStream;
    BytesRead: Integer;
    DocumentId: Int64;
    Size: Int64;
    filename: WideString;
    BufSize: Integer;
begin
    BufSize := 1024;

    try
      Mem := TMemoryStream.Create;
      GetMem( Buffer, BufSize );

      try
        RetStream := FDownloadDS.DownloadFile(Size);
        RetStream.Position := 0;

        if ( Size <> 0 ) then
        begin
          filename := 'download.fil';

          repeat
            BytesRead := RetStream.Read( Pointer( Buffer )^, BufSize );

            if ( BytesRead > 0 ) then
            begin
              Mem.WriteBuffer( Pointer( Buffer )^, BytesRead );
            end;

            lStatus.Caption := IntToStr( Mem.Size ) + '/' + IntToStr( Size );
            Application.ProcessMessages;

          until ( BytesRead < BufSize );

          if ( Size <> Mem.Size ) then
          begin
            raise Exception.Create( 'Error downloading file...' );
          end;
        end
        else
        begin
          lStatus.Caption := '';
        end;
      finally
        FreeMem( Buffer, BufSize );
        FreeAndNIl(Mem);
      end;
    except
      on E: Exception do
      begin
        lErrorMessage.Caption := PChar( E.ClassName + ': ' + E.Message );
      end;
    end;
end;

您可以根据需要调整BufSize的大小。在我尝试这种方法之前,我一直无法获取流的大小。我尝试了XE2,似乎没有同样的问题,但那时我正在上传。可能有更好的方法来检索流的大小。如果我很快得到答案,我会让您知道...
另外 - 我还没有弄清楚如何在服务器端显示进度条。我仍在努力解决这个问题。
希望这可以帮到您!如果您有任何问题,请告诉我!

不,我没有遇到任何延迟。我已经下载了各种大小的文件(最大达到5兆字节)。进度指示器运作正常。我知道这听起来很奇怪,但有时我在某些端口上使用DataSnap时会遇到问题。我更换了另一个端口,一切都正常了。不幸的是,我从未有机会找出原因,否则我会告诉你的。你的文件大小是多少?你尝试过什么样的BufSize高低范围? - aknapple
我正在测试的文件大小略小于5兆。我已经创建了与您相同的DataSnap方法,而且在客户端可以使用进度条读取流之前,方法调用需要大约40秒才能完成。我目前只是使用HTTP上的8080端口,使用TDSRESTConnection来调用DataSnap方法。您是如何连接到DataSnap服务的? - Jonathan Wareham
我还没有机会看DataSnap的REST功能。我正在使用默认的211端口和客户端TSQLConnection进行TCP连接。我遇到问题的应用程序设置为9999端口(另一个应用程序设置为9998),问题消失了。我认为这是我的ISP的过滤机制。另外,我正在使用XE2 Update 3。你有这个版本吗?你在处理JSON方面有什么进展吗?我也必须修复一些问题,但我无法想象它会影响你的下载。如果你有一个小的样本,我会构建它,并查看是否存在相同的问题。 - aknapple
顺便说一句 - 我可能会在当前项目中加入安全性。看起来效果很好,但我还没有将其投入产品中。另外一个想法是 - 我的TDSServerClass的LifeCycle属性设置为Session。 - aknapple
1
嗨,好的,这一定是一个REST问题。我创建了一个新的Delphi DS客户端,使用TSQLConnection(DBExpress)连接到同一个DS服务器,猜猜看——方法调用在1秒内返回了流。问题是,我正在编写的目标客户端应用程序是为Android而写的,我拥有的Java代理类没有等效的TSQLConnection,只有TDSRESTConnection。需要了解是否有一种方法使REST对于流文件的行为与之相同,或者它只是因为其架构的方式。 - Jonathan Wareham
我也在使用XE2 Update 3,听说Update 4随时就要发布了。是的,我正在使用JSON对象来传递/返回方法调用中的数据,没有什么花哨的东西,只需编写一个函数将TDataSet中返回的数据转换为TJSONArray即可。 - Jonathan Wareham

0
很高兴你有一些运气!这是我不得不做的另一个修复。你可以参考这个链接https://forums.embarcadero.com/thread.jspa?threadID=66490&tstart=0 在深入了解代码后,我发现了"Data.DBXJSONReflect.pas"中的以下内容: procedure TJSONPopulationCustomizer.PrePopulate(Data: TObject; rttiContext: TRttiContext); ...
3473: rttiField.GetValue(Data).AsObject.Free;
3474: rttiField.SetValue(Data, TValue.Empty);

...

我认为应该是这样的:

3473: rttiField.SetValue(Data, TValue.Empty);
3474: rttiField.GetValue(Data).AsObject.Free;

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