web-dev-qa-db-fra.com

Comment supprimer automatiquement les fichiers temporaires en c #?

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?

53
Nifle

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.

73
Marc Gravell

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
55
DanW

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 .

18
David Grant

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.");
    }
4
user3454591

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.

3
csl

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.

3
StingyJack

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.

2
Konard

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.

0
Austin Salonen