Reputation: 507
I have a Datasnap client server application written in Delphi XE6. I am calling a server method that creates temporary file using a TFileStream, fills it with my report in TStream format from my http post, and returns it to my client. This all works as needed. However, at the end of the method, I call deleteFile method to delete the temp file from the server, but it never gets delete. What am I doing wrong?
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;
Upvotes: 3
Views: 535
Reputation: 595349
As David said, you can't delete the file until the TFileStream
is destroyed first so it can close its handle to the file. If you want the file to auto-delete when the server is done using it, you can either:
open the file with the Win32 CreateFile()
function directly so you can specify the FILE_FLAG_DELETE_ON_CLOSE
flag, and then wrap the resulting handle with the 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;
derive a new class from TFileStream
and override its destructor to delete the file:
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;
Use the TFileStreamEx
class shown in 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;
Upvotes: 5
Reputation: 612794
You cannot delete the file stream that you created until you have destroyed the file stream object. The file stream object wraps a file handle, and while the file handle exists, the file object behind it cannot be deleted.
You need to destroy the file stream object before you can delete it.
Your code has some rather odd exception handling. It swallows exceptions without discrimination in the middle of the function. Do you really want to be quite so brutal? And outside the try/except block, any exceptions will lead to the file stream object being leaked.
There's not really much point in testing whether or not the file exists before deleting it. If you succeeded in creating the file stream then you can be confident that the file exists.
Upvotes: 5