Quels sont les bons moyens de s'assurer qu'un fichier temporaire est supprimé si mon application se ferme ou se bloque? Idéalement, je voudrais obtenir un fichier temporaire, l'utiliser et ensuite l'oublier.
En ce moment, je garde une liste de mes fichiers temporaires et les supprime avec un gestionnaire d'événements qui se déclenche sur Application.ApplicationExit.
Y a-t-il une meilleure façon?
Rien n'est garanti si le processus est arrêté prématurément, cependant, j'utilise "using
" pour ce faire.
using System;
using System.IO;
sealed class TempFile : IDisposable
{
string path;
public TempFile() : this(System.IO.Path.GetTempFileName()) { }
public TempFile(string path)
{
if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path");
this.path = path;
}
public string Path
{
get
{
if (path == null) throw new ObjectDisposedException(GetType().Name);
return path;
}
}
~TempFile() { Dispose(false); }
public void Dispose() { Dispose(true); }
private void Dispose(bool disposing)
{
if (disposing)
{
GC.SuppressFinalize(this);
}
if (path != null)
{
try { File.Delete(path); }
catch { } // best effort
path = null;
}
}
}
static class Program
{
static void Main()
{
string path;
using (var tmp = new TempFile())
{
path = tmp.Path;
Console.WriteLine(File.Exists(path));
}
Console.WriteLine(File.Exists(path));
}
}
Maintenant, lorsque le TempFile
est supprimé ou récupéré, le fichier est supprimé (si possible). Vous pouvez évidemment l'utiliser aussi étroitement que vous le souhaitez, ou dans une collection quelque part.
Pensez à utiliser l'indicateur FileOptions.DeleteOnClose:
using (FileStream fs = new FileStream(Path.GetTempFileName(),
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None,
4096, FileOptions.RandomAccess | FileOptions.DeleteOnClose))
{
// temp file exists
}
// temp file is gone
Vous pouvez P/Invoke CreateFile
et passer le FILE_FLAG_DELETE_ON_CLOSE
drapeau. Cela indique à Windows de supprimer le fichier une fois que tous les descripteurs sont fermés. Voir aussi: Win32 CreateFile
docs .
J'utiliserais la classe .NET TempFileCollection
, car elle est intégrée, disponible dans les anciennes versions de .NET, et implémente l'interface IDisposable
et nettoie donc après elle-même si elle est utilisée par exemple en conjonction avec le "using"
mot-clé.
Voici un exemple qui extrait du texte d'une ressource incorporée (ajouté via les pages de propriétés des projets -> onglet Ressources comme décrit ici: Comment incorporer un fichier texte dans un assemblage .NET? , puis définissez sur "EmbeddedResource"
dans les paramètres de propriété du fichier incorporé).
// Extracts the contents of the embedded file, writes them to a temp file, executes it, and cleans up automatically on exit.
private void ExtractAndRunMyScript()
{
string vbsFilePath;
// By default, TempFileCollection cleans up after itself.
using (var tempFiles = new System.CodeDom.Compiler.TempFileCollection())
{
vbsFilePath= tempFiles.AddExtension("vbs");
// Using IntelliSense will display the name, but it's the file name
// minus its extension.
System.IO.File.WriteAllText(vbsFilePath, global::Instrumentation.Properties.Resources.MyEmbeddedFileNameWithoutExtension);
RunMyScript(vbsFilePath);
}
System.Diagnostics.Debug.Assert(!File.Exists(vbsFilePath), @"Temp file """ + vbsFilePath+ @""" has not been deleted.");
}
Je ne suis pas principalement un programmeur C #, mais en C++ j'utiliserais RAII pour cela. Il y a quelques conseils sur l'utilisation d'un comportement de type RAII en C # en ligne, mais la plupart semblent utiliser le finaliseur - ce qui n'est pas déterministe.
Je pense qu'il existe certaines fonctions du SDK Windows pour créer des fichiers temporaires, mais je ne sais pas s'ils sont supprimés automatiquement à la fin du programme. Il y a la fonction GetTempPath , mais les fichiers ne sont supprimés que lorsque vous vous déconnectez ou redémarrez IIRC.
P.S. La documentation du destructeur C # indique que vous pouvez et devez y libérer des ressources, ce que je trouve un peu étrange. Si c'est le cas, vous pouvez simplement supprimer le fichier temporaire dans le destructeur, mais encore une fois, cela pourrait ne pas être complètement déterministe.
C'est agréable de voir que vous voulez être responsable, mais si les fichiers ne sont pas énormes (> 50 Mo), vous seriez en ligne avec tout le monde (MS inclus) en les laissant dans le répertoire temporaire. L'espace disque est abondant.
Comme l'a signalé csl, le GetTempPath est la voie à suivre. Les utilisateurs qui manquent d'espace pourront exécuter le nettoyage du disque et vos fichiers (ainsi que tous les autres) seront nettoyés.
J'utilise une solution plus fiable:
using System.IO;
using System.Reflection;
namespace Konard.Helpers
{
public static partial class TemporaryFiles
{
private const string UserFilesListFilenamePrefix = ".used-temporary-files.txt";
static private readonly object UsedFilesListLock = new object();
private static string GetUsedFilesListFilename()
{
return Assembly.GetEntryAssembly().Location + UserFilesListFilenamePrefix;
}
private static void AddToUsedFilesList(string filename)
{
lock (UsedFilesListLock)
{
using (var writer = File.AppendText(GetUsedFilesListFilename()))
writer.WriteLine(filename);
}
}
public static string UseNew()
{
var filename = Path.GetTempFileName();
AddToUsedFilesList(filename);
return filename;
}
public static void DeleteAllPreviouslyUsed()
{
lock (UsedFilesListLock)
{
var usedFilesListFilename = GetUsedFilesListFilename();
if (!File.Exists(usedFilesListFilename))
return;
using (var listFile = File.Open(usedFilesListFilename, FileMode.Open))
{
using (var reader = new StreamReader(listFile))
{
string tempFileToDelete;
while ((tempFileToDelete = reader.ReadLine()) != null)
{
if (File.Exists(tempFileToDelete))
File.Delete(tempFileToDelete);
}
}
}
// Clean up
using (File.Open(usedFilesListFilename, FileMode.Truncate)) { }
}
}
}
}
Chaque fois que vous avez besoin d'un fichier temporaire, utilisez:
var tempFile = TemporaryFiles.UseNew();
Pour être sûr que tous les fichiers temporaires sont supprimés après la fermeture de l'application ou le blocage de l'application
TemporaryFiles.DeleteAllPreviouslyUsed();
au début de l'application.
Vous pouvez lancer un thread au démarrage qui supprimera les fichiers qui existent alors qu'ils "ne devraient pas" se remettre de votre crash.