在 Delphi XE6 中调用 datasnap 服务器方法后删除文件
File delete after datasnap server method call in Delphi XE6
我有一个用 Delphi XE6 编写的 Datasnap 客户端服务器应用程序。我正在调用一个服务器方法,该方法使用 TFileStream 创建临时文件,用我的 TStream 格式的报告从我的 http post 填充它,然后 returns 到我的客户端。这一切都按需要工作。然而,在该方法的最后,我调用 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;
在销毁文件流对象之前,您不能删除您创建的文件流。文件流对象包裹了一个文件句柄,当文件句柄存在时,其后面的文件对象不能被删除。
您需要销毁文件流对象才能删除它。
您的代码有一些相当奇怪的异常处理。它在函数中间不加区别地吞下异常。你真的想要这么残忍吗?而在try/except块之外,任何异常都会导致文件流对象被泄露。
在删除文件之前测试文件是否存在并没有多大意义。如果您成功创建了文件流,那么您可以确信该文件存在。
正如 David 所说,只有先销毁 TFileStream
才能删除文件,这样它才能关闭文件的句柄。如果您希望文件在服务器使用完后自动删除,您可以:
直接用Win32CreateFile()
函数打开文件所以可以指定FILE_FLAG_DELETE_ON_CLOSE
标志,然后用THandleStream
包裹得到的句柄class.
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
派生一个新的 class 并覆盖其析构函数以删除文件:
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;
使用 TFileStreamEx
class this forum answer:
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;
我有一个用 Delphi XE6 编写的 Datasnap 客户端服务器应用程序。我正在调用一个服务器方法,该方法使用 TFileStream 创建临时文件,用我的 TStream 格式的报告从我的 http post 填充它,然后 returns 到我的客户端。这一切都按需要工作。然而,在该方法的最后,我调用 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;
在销毁文件流对象之前,您不能删除您创建的文件流。文件流对象包裹了一个文件句柄,当文件句柄存在时,其后面的文件对象不能被删除。
您需要销毁文件流对象才能删除它。
您的代码有一些相当奇怪的异常处理。它在函数中间不加区别地吞下异常。你真的想要这么残忍吗?而在try/except块之外,任何异常都会导致文件流对象被泄露。
在删除文件之前测试文件是否存在并没有多大意义。如果您成功创建了文件流,那么您可以确信该文件存在。
正如 David 所说,只有先销毁 TFileStream
才能删除文件,这样它才能关闭文件的句柄。如果您希望文件在服务器使用完后自动删除,您可以:
直接用Win32
CreateFile()
函数打开文件所以可以指定FILE_FLAG_DELETE_ON_CLOSE
标志,然后用THandleStream
包裹得到的句柄class.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
派生一个新的 class 并覆盖其析构函数以删除文件: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;
使用
TFileStreamEx
class this forum answer: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;