Как я могу обрабатывать временные файлы? - PullRequest
4 голосов
/ 03 июня 2009

В моем приложении мои пользователи могут импортировать файлы (pdf / xls / doc) в таблицу или экспортировать их в папку. Теперь я хочу открыть эти файлы напрямую.

Пока я могу: - получить уникальное имя - сохранить файл BLOB в сгенерированный файл - открой его

Проблема в том, что я не знаю, как удалить (или обновить) файл, после чего файл будет закрыт пользователем.

Я буду очень рад, если кто-нибудь сможет мне помочь в этом:)

Вот снимок моего кода:

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;

Ответы [ 7 ]

9 голосов
/ 04 июня 2009

К сожалению, сейчас есть 4 отзыва за этот ответ Реми Лебо , когда техника просто не будет работать с большинством приложений. Может быть, кто-то из разработчиков мог опубликовать фрагмент кода, который позволяет открыть файл PDF с помощью Acrobat Reader, пока файл еще открыт с флагом FILE_FLAG_DELETE_ON_CLOSE?

В любом случае, вы можете объединить некоторые советы здесь для достижения наилучших результатов:

  • Наличие внутреннего списка временных файлов, используемых вашим приложением.
  • При завершении работы программы пройдите список временных файлов и попробуйте удалить их. Если это не удается для некоторых из них (поскольку они все еще открыты во внешнем приложении), зарегистрируйте их для удаления при перезагрузке с помощью , код gabr дал вам .
  • Всякий раз, когда вам нужен новый временный файл, сначала просмотрите внутренний список файлов и попробуйте повторно использовать один из них. Создайте новый файл (и добавьте его имя в список) только в случае неудачи.

Я бы предпочел этот подход для регистрации всех файлов для удаления при перезагрузке, потому что я не уверен, сколько временных файлов может открыть ваше приложение - возможно, существует ограничение на количество файлов, которые можно зарегистрировать с помощью MOVEFILE_DELAY_UNTIL_REBOOT? Это общесистемный ресурс, который я бы использовал только в редких случаях.

7 голосов
/ 03 июня 2009

Одной из возможностей будет добавление каждого временного файла в список файлов, которые удаляются при запуске системы.

На платформе Windows NT (начиная с Windows 2000) вы можете просто вызвать функцию MoveFileEx со вторым параметром (назначение), равным nil, и с флагом MOVEFILE_DELAY_UNTIL_REBOOT.

В Windows 9x это намного сложнее. Вы должны отредактировать файл% WINDIR% \ wininit.ini и записать запись в раздел [Rename].

MSDN запись Как переместить файлы, которые используются в настоящее время описывает оба метода.

Функция DSiMoveOnReboot (часть бесплатной библиотеки DSiWin32 ) обрабатывает обе операционные системы. Если вы передадите пустую строку в качестве второго параметра, при перезагрузке будет удален исходный файл.

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 }
3 голосов
/ 03 июня 2009

Используйте функцию Win32 API CreateFile (), чтобы открыть файл, указав флаг FILE_FLAG_DELETE_ON_CLOSE, а затем передайте полученный дескриптор объекту THandleStream, чтобы вы все еще могли использовать SaveToStream ().

Кроме того, в вашем коде есть ошибка - вы передаете неверный тип дескриптора в ShellExecute (). Он ожидает дескриптор окна, но вместо этого вы передаете дескриптор файла, и, что еще хуже, вы получаете доступ к дескриптору файла после того, как вы уже освободили TFileStream, закрывая дескриптор.

1 голос
/ 03 июня 2009

Если я правильно помню, для CreateFile есть флаг, который сообщает Windows, что он должен удалить файл после закрытия последнего дескриптора. Итак, создайте файл как обычно, закройте и снова откройте его, используя share, deny none и флаг, упомянутый выше. Затем пусть внешнее приложение откроет его и закроет самостоятельно. Это должно привести к тому, что Windows удалит файл, как только внешнее приложение закроет его.

(я не пробовал это.)

1 голос
/ 03 июня 2009

Может быть, вы можете сохранить их в какой-нибудь известной для вас папке (например, в подпапке в папке TEMP с названием вашего приложения) и очистить содержимое этой папки, когда пользователь в следующий раз загрузит ваше приложение? Или вы можете установить дополнительную утилиту очистки и настроить ее запуск в автозапуске.
Еще одна идея об очистке файлов после перезагрузки - вы можете очистить все в своей подпапке при запуске, или составить список файлов, которые вы создали со временем последнего изменения, последним размером, сохранить этот список в файле XML, а затем удалить или обновить сравнение содержимое вашей временной подпапки с данными файла из этого списка?

0 голосов
/ 04 июня 2009

На самом деле наше приложение создает файлы в определенной временной папке. При закрытии приложения файлы удаляются. Если приложение закрывается некорректно, при следующем выполнении (при закрытии) все файлы удаляются.

Кроме того, вы можете запустить фоновый процесс, чтобы удалить файлы, которые больше не открыты. ShellExecute возвращает дескриптор (внутренне связывает этот дескриптор с FileName). Этот фоновый процесс должен проверить дескриптор несуществующего процесса и удалить связанные файлы.

Извините за плохой английский. ; -)

Привет.

0 голосов
/ 03 июня 2009

в Unix-подобных ОС обычная уловка - открыть ее и немедленно удалить. он не будет отображаться в каталоге, но данные по-прежнему распределяются для процесса (ов), которые удерживают его открытым. как только он будет закрыт (или, если угодно, из-за смерти процесса), файловая система освободит пространство.

это не хак, он документирован и поддерживается (следствие того, что дескрипторы открытого файла считаются «ссылкой» на файл, точно так же, как записи каталога).

может быть, есть какой-то похожий трюк на окнах? Кажется, я помню, что NTFS поддерживает несколько ссылок на один и тот же файл (нет, это не ярлыки). в этом случае может сработать удаление файла, но все еще привязка к последней ссылке в качестве эфемерного ресурса.

очевидно, я просто размышляю здесь ...

...