【问题标题】:File delete after datasnap server method call in Delphi XE6在 Delphi XE6 中调用 datasnap 服务器方法后文件删除
【发布时间】:2016-06-09 13:56:48
【问题描述】:

我有一个用 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;

【问题讨论】:

    标签: delphi datasnap


    【解决方案1】:

    正如大卫所说,在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;
      

    【讨论】:

    • 嗨 Remy,我尝试了你的两种方法。第一个 - 我仍然需要关闭服务器应用程序才能删除文件。第二个,没有任何内容被删除 - 即使我关闭服务器或客户端。
    • 这表明流没有被释放。你检查了吗?
    • 我做了,它正在被释放。
    • 看起来我需要在您的示例 1 中关闭文件句柄:CloseHandle(h);
    • @John:我修复了句柄泄漏。
    【解决方案2】:

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

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

    您的代码有一些相当奇怪的异常处理。它在函数中间不加区别地吞下异常。你真的要这么残忍吗?而在try/except块之外,任何异常都会导致文件流对象被泄露。

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

    【讨论】:

    • 嗨,大卫,感谢您的回复。由于 FileStream 是在服务器方法中创建并作为 TStream 返回给客户端的,那么在该服务器方法中删除 FileStream 是否安全?您能帮助我更多地了解我在异常处理方面出了什么问题吗?
    • 我无法真正评论问题之外的内容。我想知道如果您只想要暂时的东西,为什么要使用文件流。为什么不使用内存流。至于异常处理,你为什么选择吞下异常?你写了那个代码。原因是什么?你为什么要处理异常?这里的基本原则是你不应该经常处理异常。让他们漂浮到知道如何处理他们的东西上。无论如何,我想我解释了为什么 DeleteFile 失败了,这不是你的问题吗?
    猜你喜欢
    • 2014-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多