我有一个用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;
答案 0 :(得分:5)
在销毁文件流对象之前,无法删除您创建的文件流。文件流对象包装文件句柄,当文件句柄存在时,不能删除它后面的文件对象。
您需要销毁文件流对象才能删除它。
您的代码有一些奇怪的异常处理。它在函数中间不受歧视地吞下异常。你真的想要如此野蛮吗?在try / except块之外,任何异常都会导致文件流对象泄露。
在删除文件之前测试文件是否存在并没有太大意义。如果您成功创建了文件流,那么您可以确信该文件存在。
答案 1 :(得分:5)
正如David所说,在TFileStream
首先被销毁之前你无法删除文件,因此它可以关闭文件的句柄。如果您希望在使用服务器完成后自动删除文件,您可以:
直接使用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;
从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;
使用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;