How can I handle temporary files?

In my application, my users can import files (pdf / xls / doc) into a table or export them to a folder. Now I want to open these files directly.

So far I can: - get a unique name - save the blob file to the generated file - open it

The problem is that I do not know how to delete (or update) the file after the file is closed by the user.

I will be very happy if someone can help me with this :)

Here is a snapshot of my code:

procedure OpenTemporaryFile(AFileExtension: String; AKey: Integer; AMyConnection: TMyConnection); Var qrDocuments : TMyQuery ; TmpName,ExtName: string; TempFileName: TFileStream; begin //Generate an unique tmp file located into user temp folder TmpName:= FileGetTempName('~SI'); ExtName:= ChangeFileExt(TmpName, AFileExtension); //Change files extension so that Shellexecute will be able to open the file RenameFile(TmpName,ExtName ); //Creating the FileStream (data is fetched from an blob field) TempFileName := TFileStream.Create(ExtName, fmOpenReadWrite ); qrDocuments := TMyQuery.create(nil); try qrDocuments.Connection := AMyConnection; qrDocuments.Close; qrDocuments.SQL.Clear; qrDocuments.SQL.Text:='Select Id,FileName,Data from files where Id = :prId And Data IS NOT NULL'; qrDocuments.ParamByName('prId').AsInteger := AKey; qrDocuments.open; TBlobField(qrDocuments.FieldByName('Data')).SaveToStream(TempFileName); finally TempFileName.Free; qrDocuments.free; end; ShellExecute(Application.Handle, 'open', Pchar(ExtName), '', '', SW_SHOWNORMAL); DeleteFile( ExtName); end; 
+4
source share
7 answers

Unfortunately, now there are 4 upvotes for this Remy Lebeau answer , when the technique simply will not work with most applications. Perhaps one of the activists could post a piece of code that allows you to open a PDF using Acrobat Reader while the file is still open with the FILE_FLAG_DELETE_ON_CLOSE flag?

In any case, you can combine some tips here to get the best results:

  • Have an internal list of temporary files that your application uses.
  • When turning off the program, view the list of temporary files and try to delete them. If this fails for some of them (because they are still open in an external application), register them for deletion at reboot using the gabr code provided to you .
  • Whenever you need a new temporary file, first go through your internal list of files and try reusing one of them. Create a new file (and add its name to the list) only if that doesn't work.

I would prefer this approach to register all files for deletion on reboot, because I'm not sure how many temporary files your application can open - maybe there is a limit on the number of files that can be registered with MOVEFILE_DELAY_UNTIL_REBOOT ? This is a system resource that I would use only sparingly.

+9
source

One possibility would be to add each temporary file to the list of files that were deleted during system startup.

On the Windows NT platform (starting with Windows 2000), you can simply call the MoveFileEx function with the second parameter (destination) set to zero and with the MOVEFILE_DELAY_UNTIL_REBOOT flag.

On Windows 9x, this is much more complicated. You must edit the% WINDIR% \ wininit.ini file and write the entry to the [Rename] section.

MSDN Record How to move files that are currently in use ... describes both methods.

The DSiMoveOnReboot function (part of the free DSiWin32 ) handles both operating systems. If you pass an empty string as the second parameter, it will delete the original file upon reboot.

 function DSiMoveOnReboot(const srcName, destName: string): boolean; var wfile: string; winit: text; wline: string; cont : TStringList; i : integer; found: boolean; dest : PChar; begin if destName = '' then dest := nil else dest := PChar(destName); if DSiIsWinNT then Result := MoveFileEx(PChar(srcName), dest, MOVEFILE_DELAY_UNTIL_REBOOT) else Result := false; if not Result then begin // not NT, write a Rename entry to WININIT.INI wfile := DSiGetWindowsFolder+'\wininit.ini'; if FileOpenSafe(wfile,winit,500,120{one minute}) then begin try cont := TStringList.Create; try Reset(winit); while not Eof(winit) do begin Readln(winit,wline); cont.Add(wline); end; //while if destName = '' then wline := 'NUL='+srcName else wline := destName+'='+srcName; found := false; for i := 0 to cont.Count - 1 do begin if UpperCase(cont[i]) = '[RENAME]' then begin cont.Insert(i+1,wline); found := true; break; end; end; //for if not found then begin cont.Add('[Rename]'); cont.Add(wline); end; Rewrite(winit); for i := 0 to cont.Count - 1 do Writeln(winit,cont[i]); Result := true; finally cont.Free; end; finally Close(winit); end; end; end; end; { DSiMoveOnReboot } 
+7
source

Use the CreateFile () function for the Win32 API to open the file by specifying the FILE_FLAG_DELETE_ON_CLOSE flag, and then pass the resulting handle to the THandleStream object so that you can still use SaveToStream ().

In addition, there is an error in your code - you are passing the wrong type of descriptor ShellExecute (). It expects a window descriptor, but instead you pass a file descriptor, and, even worse, you access the file descriptor after you have already released TFileStream, thereby closing the descriptor.

+3
source

Perhaps you can save them in a folder known to you (for example, a subfolder in the TEMP folder with the name of your application) and clear the contents of this folder if the user downloads your application next time? Or you can install an additional cleaning utility and configure it to run at startup.
Another idea about clearing files after rebooting is that you can clear everything in your subfolder at startup or make a list of files created with the last change time, last size, save this list in an XML file, and then delete or update the comparison contents of your temp subfolder with file data from this list?

+1
source

If I remember correctly, there is a flag for CreateFile that tells Windows that it should delete the file after the last handle to it was closed. So, create the file normally, close it and open it, and share deny none and the flag mentioned above. Then open the external application and close it yourself. This will cause Windows to delete the file after closing the external application.

(I have not tried this.)

+1
source

on Unix-like OSes, the usual trick is to open it and remove it immediately. it will not appear in the directory, but the data is still allocated to the process (s) that keeps it open. as soon as it is closed (or beautifully using a process that dies), the file system will return space.

this is not a hack, it is documented and maintained (due to the fact that open files are treated as a "link" to the file, as well as directory entries).

Maybe there is some kind of trick in the windows? I seem to remember that NTFS supports multiple links to the same file (no, the shortcuts are not the same). if so, deleting the file but still hanging until the last link as an ephemeral resource may work.

obviously i'm just thinking here ...

0
source

In fact, our application creates files in a special temp folder. When the application is closed, users will be deleted. If the application is not closed correctly, the next execution (when closing) of all files will be deleted.

In addition, you can run a background process to delete files that are no longer open. ShellExecute returns Handle (internally associates this handle with FileName). This background process should check the handle of a process that does not exist and delete the associated files.

Sorry for the poor English .; -)

Sincerely.

0
source

Source: https://habr.com/ru/post/1285685/


All Articles