File: src\libraries\Common\src\System\IO\TempFileCollection.cs
Web Access
Project: src\src\libraries\System.CodeDom\src\System.CodeDom.csproj (System.CodeDom)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.IO;
 
// We keep this class in Common to allow utilizing it without taking a dependency on System.CodeDom
#if CODEDOM
namespace System.CodeDom.Compiler
#else
namespace System.IO.Internal
#endif
{
    // Explicitly not [Serializable], so as to avoid accidentally deleting
    // files specified in a serialized payload.
 
#if CODEDOM
    public
#else
    internal sealed
#endif
    class TempFileCollection : ICollection, IDisposable
    {
        private string _basePath;
        private readonly string _tempDir;
        private readonly Hashtable _files;
        private bool _createdTempDirectory;
 
        public TempFileCollection() : this(null, false)
        {
        }
 
        public TempFileCollection(string tempDir) : this(tempDir, false)
        {
        }
 
        public TempFileCollection(string tempDir, bool keepFiles)
        {
            KeepFiles = keepFiles;
            _tempDir = tempDir;
            _files = new Hashtable(StringComparer.OrdinalIgnoreCase);
        }
 
        void IDisposable.Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
#pragma warning disable IDE0060
#if CODEDOM
        protected virtual
#else
        internal
#endif
        void Dispose(bool disposing)
        {
            SafeDelete();
        }
#pragma warning restore IDE0060
 
        ~TempFileCollection()
        {
            Dispose(false);
        }
 
        public string AddExtension(string fileExtension) => AddExtension(fileExtension, KeepFiles);
 
        public string AddExtension(string fileExtension, bool keepFile)
        {
            if (string.IsNullOrEmpty(fileExtension))
            {
                throw new ArgumentException(SR.Format(SR.InvalidNullEmptyArgument, nameof(fileExtension)), nameof(fileExtension));
            }
 
            string fileName = BasePath + "." + fileExtension;
            AddFile(fileName, keepFile);
            return fileName;
        }
 
        public void AddFile(string fileName, bool keepFile)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException(SR.Format(SR.InvalidNullEmptyArgument, nameof(fileName)), nameof(fileName));
            }
 
            if (_files[fileName] != null)
            {
                throw new ArgumentException(SR.Format(SR.DuplicateFileName, fileName), nameof(fileName));
            }
 
            _files.Add(fileName, keepFile);
        }
 
        public IEnumerator GetEnumerator() => _files.Keys.GetEnumerator();
 
        IEnumerator IEnumerable.GetEnumerator() => _files.Keys.GetEnumerator();
 
        void ICollection.CopyTo(Array array, int start) => _files.Keys.CopyTo(array, start);
 
        public void CopyTo(string[] fileNames, int start) => _files.Keys.CopyTo(fileNames, start);
 
        public int Count => _files.Count;
 
        int ICollection.Count => _files.Count;
 
        object ICollection.SyncRoot => null;
 
        bool ICollection.IsSynchronized => false;
 
        public string TempDir => _tempDir ?? string.Empty;
 
        public string BasePath
        {
            get
            {
                EnsureTempNameCreated();
                return _basePath;
            }
        }
 
        private void EnsureTempNameCreated()
        {
            if (_basePath == null)
            {
                string tempFileName;
                bool uniqueFile;
                int retryCount = 5000;
                do
                {
                    _basePath = Path.Combine(
                        string.IsNullOrEmpty(TempDir) ? GetTempDirectory() : TempDir,
                        Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));
                    tempFileName = _basePath + ".tmp";
 
                    try
                    {
                        new FileStream(tempFileName, FileMode.CreateNew, FileAccess.Write).Dispose();
                        uniqueFile = true;
                    }
                    catch (IOException ex)
                    {
                        retryCount--;
                        if (retryCount == 0 || ex is DirectoryNotFoundException)
                        {
                            throw;
                        }
                        uniqueFile = false;
                    }
                } while (!uniqueFile);
                _files.Add(tempFileName, KeepFiles);
            }
        }
 
#if NET
        private string GetTempDirectory()
        {
            _createdTempDirectory = true;
            return Directory.CreateTempSubdirectory().FullName;
        }
#else
        private static string GetTempDirectory()
        {
            return Path.GetTempPath();
        }
#endif
 
        public bool KeepFiles { get; set; }
 
        private bool KeepFile(string fileName)
        {
            object keep = _files[fileName];
            return keep != null ? (bool)keep : false;
        }
 
        public void Delete()
        {
            SafeDelete();
        }
 
        private static void Delete(string fileName)
        {
            try
            {
                File.Delete(fileName);
            }
            catch
            {
                // Ignore all exceptions
            }
        }
 
        internal void SafeDelete()
        {
            bool allFilesDeleted = true;
 
            if (_files != null && _files.Count > 0)
            {
                string[] fileNames = new string[_files.Count];
                _files.Keys.CopyTo(fileNames, 0);
                foreach (string fileName in fileNames)
                {
                    if (!KeepFile(fileName))
                    {
                        Delete(fileName);
                        _files.Remove(fileName);
                    }
                    else
                    {
                        allFilesDeleted = false;
                    }
                }
            }
 
            // if we created a temp directory, delete it and clear the basePath, so a new directory will be created for the next request.
            if (_createdTempDirectory && allFilesDeleted)
            {
                try
                {
                    Directory.Delete(Path.GetDirectoryName(BasePath));
                }
                catch
                {
                    // Ignore all exceptions
                }
 
                _createdTempDirectory = false;
                _basePath = null;
            }
        }
    }
}