在Delphi XE6中调用datasnap服务器方法后删除文件

时间:2016-06-09 13:56:49

标签: delphi datasnap

我有一个用Delphi XE6编写的Datasnap客户端服务器应用程序。我正在调用一个使用TFileStream创建临时文件的服务器方法,用我的http帖子中的TStream格式填充我的报告,并将其返回给我的客户端。这一切都按需要运作。但是,在方法结束时,我调用deleteFile方法从服务器中删除临时文件,但它永远不会被删除。我做错了什么?

var
  r,f: String;
  SS: TStringStream;
  Uid: TGuid;
begin
   CreateGuid(Uid);
   f:= ChangeFileExt(GuidToString(Uid),'.rpt');
   result:= TFileStream.Create(f, fmCreate or fmOpenWrite);

  r := getRunReportJSON(ARunReportObj);
  SS := TStringStream.Create(r, TEncoding.ASCII);
  try
    try      
      ServerContainer1.idHttp1.Post(gUrl, SS, result);
      Result.Position:= 0;
    except
    end;
  finally
    SS.Free;
    if FileExists(f) then
      DeleteFile(f)
  end;
end;

2 个答案:

答案 0 :(得分:5)

在销毁文件流对象之前,无法删除您创建的文件流。文件流对象包装文件句柄,当文件句柄存在时,不能删除它后面的文件对象。

您需要销毁文件流对象才能删除它。

您的代码有一些奇怪的异常处理。它在函数中间不受歧视地吞下异常。你真的想要如此野蛮吗?在try / except块之外,任何异常都会导致文件流对象泄露。

在删除文件之前测试文件是否存在并没有太大意义。如果您成功创建了文件流,那么您可以确信该文件存在。

答案 1 :(得分:5)

正如David所说,在TFileStream首先被销毁之前你无法删除文件,因此它可以关闭文件的句柄。如果您希望在使用服务器完成后自动删除文件,您可以:

  1. 直接使用Win32 CreateFile()函数打开文件,以便指定FILE_FLAG_DELETE_ON_CLOSE标志,然后使用THandleStream类包装生成的句柄。

    type
      TMyHandleStream = class(THandleStream)
      public
        destructor Destroy; override;
      end;
    
    destructor TMyHandleStream.Destroy;
    begin
      inherited;
      CloseHandle(Handle);
    end;
    
    var
      h: THandle;
      r, f: String;
      SS: TStringStream;
      Uid: TGuid;
    begin
      CreateGuid(Uid);
      f := 'C:\some folder\' + GuidToString(Uid) + '.rpt';
      h := Windows.CreateFile(PChar(f), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0);
      if h = INVALID_HANDLE_VALUE then RaiseLastOSError;
      Result := TMyHandleStream.Create(h);
      try
        r := getRunReportJSON(ARunReportObj);
        SS := TStringStream.Create(r, TEncoding.ASCII);
        try
          ServerContainer1.IdHTTP1.Post(gUrl, SS, Result);
        finally
          SS.Free;
        end;
        Result.Position := 0;
      except
        Result.Free;
        raise;
      end;
    end;
    
  2. TFileStream派生一个新类并覆盖其析构函数以删除该文件:

    type
      TMyFileStream = class(TFileStream)
      public
        destructor Destroy; override;
      end;
    
    destructor TMyFileStream.Destroy;
    begin
      inherited;
      DeleteFile(Self.FileName);
    end;
    

    var
      r, f: String;
      SS: TStringStream;
      Uid: TGuid;
    begin
      CreateGuid(Uid);
      f := 'C:\some folder\' + GuidToString(Uid) + '.rpt';
      Result := TMyFileStream.Create(f, fmCreate or fmOpenReadWrite);
      try
        r := getRunReportJSON(ARunReportObj);
        SS := TStringStream.Create(r, TEncoding.ASCII);
        try
          ServerContainer1.IdHTTP1.Post(gUrl, SS, Result);
        finally
          SS.Free;
        end;
        Result.Position := 0;
      except
        Result.Free;
        raise;
      end;
    end;
    
  3. 使用this forum answer中显示的TFileStreamEx类:

    type
      TFileStreamEx = class(THandleStream)
      public
        constructor Create(const FileName: string; Mode: Word; flags: DWORD = FILE_ATTRIBUTE_NORMAL);
        destructor Destroy; override;
      end;
    
    constructor TFileStreamEx.Create(const FileName: string; Mode: Word; flags: DWORD = FILE_ATTRIBUTE_NORMAL);
    const
      AccessMode: array[0..2] of LongWord = (
        GENERIC_READ,
        GENERIC_WRITE,
        GENERIC_READ or GENERIC_WRITE);
      ShareMode: array[0..4] of LongWord = (
        0,
        0,
        FILE_SHARE_READ,
        FILE_SHARE_WRITE,
        FILE_SHARE_READ or FILE_SHARE_WRITE);
    begin
      if Mode = fmCreate then
      begin
        inherited Create(
          CreateFile(
            PChar(FileName),
            GENERIC_READ or GENERIC_WRITE,
            0,
            nil,
            CREATE_ALWAYS,
            flags,
            0
          )
        );
        if Handle = INVALID_HANDLE then
          raise EFCreateError.CreateFmt(SFCreateError, [FileName]);
      end
      else
      begin
        inherited Create(
          CreateFile(
            PChar(FileName),
            AccessMode[Mode and 3],
            ShareMode[(Mode and $F0) shr 4],
            nil,
            OPEN_EXISTING,
            flags,
            0
          )
        );
        if Handle = INVALID_HANDLE then
          raise EFOpenError.CreateFmt(SFOpenError, [FileName]);
      end;
    end;
    
    destructor TFileStreamEx.Destroy;
    begin
      if Handle <> INVALID_HANDLE then CloseHandle(Handle);
    end;
    

    var
      r, f: String;
      SS: TStringStream;
      Uid: TGuid;
    begin
      CreateGuid(Uid);
      f := 'C:\some folder\' + GuidToString(Uid) + '.rpt';
      Result := TFileStreamEx.Create(f, fmCreate or fmOpenReadWrite, FILE_FLAG_DELETE_ON_CLOSE);
      try
        r := getRunReportJSON(ARunReportObj);
        SS := TStringStream.Create(r, TEncoding.ASCII);
        try
          ServerContainer1.IdHTTP1.Post(gUrl, SS, Result);
        finally
          SS.Free;
        end;
        Result.Position := 0;
      except
        Result.Free;
        raise;
      end;
    end;