Represents an in memory collection of keys and values whose operations are concurrent.
Exemple #1
0
        /// <summary>
        /// Returns a value indicating whether the original file is new or has been updated.
        /// </summary>
        /// <returns>
        /// True if the the original file is new or has been updated; otherwise, false.
        /// </returns>
        internal async Task <bool> IsNewOrUpdatedFileAsync()
        {
            string      path      = this.CachedPath;
            bool        isUpdated = false;
            CachedImage cachedImage;

            if (this.isRemote)
            {
                cachedImage = await CacheIndexer.GetValueAsync(path);

                if (cachedImage != null)
                {
                    // Can't check the last write time so check to see if the cached image is set to expire
                    // or if the max age is different.
                    if (this.IsExpired(cachedImage.CreationTimeUtc))
                    {
                        CacheIndexer.Remove(path);
                        isUpdated = true;
                    }
                }
                else
                {
                    // Nothing in the cache so we should return true.
                    isUpdated = true;
                }
            }
            else
            {
                // Test now for locally requested files.
                cachedImage = await CacheIndexer.GetValueAsync(path);

                if (cachedImage != null)
                {
                    FileInfo imageFileInfo = new FileInfo(this.requestPath);

                    if (imageFileInfo.Exists)
                    {
                        // Pull the latest info.
                        imageFileInfo.Refresh();

                        // Check to see if the last write time is different of whether the
                        // cached image is set to expire or if the max age is different.
                        if (!this.RoughDateTimeCompare(imageFileInfo.LastWriteTimeUtc, cachedImage.LastWriteTimeUtc) ||
                            this.IsExpired(cachedImage.CreationTimeUtc))
                        {
                            CacheIndexer.Remove(path);
                            isUpdated = true;
                        }
                    }
                }
                else
                {
                    // Nothing in the cache so we should return true.
                    isUpdated = true;
                }
            }

            return(isUpdated);
        }
Exemple #2
0
        /// <summary>
        /// Gets a value indicating whether the image is new or updated in an asynchronous manner.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        public override async Task <bool> IsNewOrUpdatedAsync()
        {
            // TODO: Before this check is performed it should be throttled. For example, only perform this check
            // if the last time it was checked is greater than 5 seconds. This would be much better for perf
            // if there is a high throughput of image requests.
            string cachedFileName = await this.CreateCachedFileNameAsync();

            // Collision rate of about 1 in 10000 for the folder structure.
            // That gives us massive scope to store millions of files.
            string pathFromKey        = string.Join("\\", cachedFileName.ToCharArray().Take(6));
            string virtualPathFromKey = pathFromKey.Replace(@"\", "/");

            this.CachedPath            = Path.Combine(this.absoluteCachePath, pathFromKey, cachedFileName);
            this.virtualCachedFilePath = Path.Combine(this.virtualCachePath, virtualPathFromKey, cachedFileName).Replace(@"\", "/");

            bool        isUpdated   = false;
            CachedImage cachedImage = CacheIndexer.Get(this.CachedPath);

            if (cachedImage == null)
            {
                FileInfo fileInfo = new FileInfo(this.CachedPath);

                if (fileInfo.Exists)
                {
                    // Pull the latest info.
                    fileInfo.Refresh();

                    cachedImage = new CachedImage
                    {
                        Key             = Path.GetFileNameWithoutExtension(this.CachedPath),
                        Path            = this.CachedPath,
                        CreationTimeUtc = fileInfo.CreationTimeUtc
                    };

                    CacheIndexer.Add(cachedImage);
                }
            }

            if (cachedImage == null)
            {
                // Nothing in the cache so we should return true.
                isUpdated = true;
            }
            else
            {
                // Check to see if the cached image is set to expire.
                if (this.IsExpired(cachedImage.CreationTimeUtc))
                {
                    CacheIndexer.Remove(this.CachedPath);
                    isUpdated = true;
                }
            }

            return(isUpdated);
        }
Exemple #3
0
        /// <summary>
        /// Adds an image to the cache.
        /// </summary>
        /// <param name="cachedPath">
        /// The path to the cached image.
        /// </param>
        public void AddImageToCache(string cachedPath)
        {
            string      key         = Path.GetFileNameWithoutExtension(cachedPath);
            CachedImage cachedImage = new CachedImage
            {
                Key             = key,
                Path            = cachedPath,
                CreationTimeUtc = DateTime.UtcNow
            };

            CacheIndexer.Add(cachedImage);
        }
Exemple #4
0
        /// <summary>
        /// Gets a value indicating whether the image is new or updated in an asynchronous manner.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        public override async Task <bool> IsNewOrUpdatedAsync()
        {
            // TODO: Before this check is performed it should be throttled. For example, only perform this check
            // if the last time it was checked is greater than 5 seconds. This would be much better for perf
            // if there is a high throughput of image requests.
            string cachedFileName = await this.CreateCachedFileNameAsync().ConfigureAwait(false);

            this.CachedPath            = CachedImageHelper.GetCachedPath(this.absoluteCachePath, cachedFileName, false, this.FolderDepth);
            this.virtualCachedFilePath = CachedImageHelper.GetCachedPath(this.virtualCachePath, cachedFileName, true, this.FolderDepth);

            bool        isUpdated   = false;
            CachedImage cachedImage = CacheIndexer.Get(this.CachedPath);

            if (cachedImage == null)
            {
                var info = new FileInfo(this.CachedPath);
                if (info.Exists)
                {
                    cachedImage = new CachedImage
                    {
                        Key             = Path.GetFileNameWithoutExtension(this.CachedPath),
                        Path            = this.CachedPath,
                        CreationTimeUtc = info.LastWriteTimeUtc
                    };

                    CacheIndexer.Add(cachedImage, this.ImageCacheMaxMinutes);
                }
            }

            if (cachedImage == null)
            {
                // Nothing in the cache so we should return true.
                isUpdated = true;
            }
            else
            {
                // Check to see if the cached image is set to expire
                // or a new file with the same name has replaced our current image
                if (this.IsExpired(cachedImage.CreationTimeUtc) || await this.IsUpdatedAsync(cachedImage.CreationTimeUtc).ConfigureAwait(false))
                {
                    CacheIndexer.Remove(this.CachedPath);
                    isUpdated = true;
                }
                else
                {
                    // Set cachedImageCreationTimeUtc so we can sender Last-Modified or ETag header when using Response.TransmitFile()
                    this.cachedImageCreationTimeUtc = cachedImage.CreationTimeUtc;
                }
            }

            return(isUpdated);
        }
Exemple #5
0
        /// <summary>
        /// Gets the <see cref="T:System.DateTime"/> set to the last write time of the file.
        /// </summary>
        /// <returns>
        /// The last write time of the file.
        /// </returns>
        internal async Task <DateTime> GetLastWriteTimeAsync()
        {
            DateTime dateTime = DateTime.UtcNow;

            CachedImage cachedImage = await CacheIndexer.GetValueAsync(this.CachedPath);

            if (cachedImage != null)
            {
                dateTime = cachedImage.LastWriteTimeUtc;
            }

            return(dateTime);
        }
Exemple #6
0
        /// <summary>
        /// Adds an image to the cache.
        /// </summary>
        /// <param name="creationAndLastWriteDateTimes">
        /// The creation and last write times.
        /// </param>
        internal void AddImageToCache(Tuple <DateTime, DateTime> creationAndLastWriteDateTimes)
        {
            string      key         = Path.GetFileNameWithoutExtension(this.CachedPath);
            CachedImage cachedImage = new CachedImage
            {
                Key              = key,
                Path             = this.CachedPath,
                CreationTimeUtc  = creationAndLastWriteDateTimes.Item1,
                LastWriteTimeUtc = creationAndLastWriteDateTimes.Item2
            };

            CacheIndexer.Add(cachedImage);
        }
Exemple #7
0
        /// <summary>
        /// Trims the cache of any expired items in an asynchronous manner.
        /// </summary>
        /// <returns>
        /// The asynchronous <see cref="Task"/> representing an asynchronous operation.
        /// </returns>
        public override async Task TrimCacheAsync()
        {
            string directory = Path.GetDirectoryName(this.CachedPath);

            if (directory != null)
            {
                DirectoryInfo directoryInfo       = new DirectoryInfo(directory);
                DirectoryInfo parentDirectoryInfo = directoryInfo.Parent;

                if (parentDirectoryInfo != null)
                {
                    // UNC folders can throw exceptions if the file doesn't exist.
                    foreach (DirectoryInfo enumerateDirectory in await parentDirectoryInfo.SafeEnumerateDirectoriesAsync())
                    {
                        IEnumerable <FileInfo> files = enumerateDirectory.EnumerateFiles().OrderBy(f => f.CreationTimeUtc);
                        int count = files.Count();

                        foreach (FileInfo fileInfo in files)
                        {
                            try
                            {
                                // If the group count is equal to the max count minus 1 then we know we
                                // have reduced the number of items below the maximum allowed.
                                // We'll cleanup any orphaned expired files though.
                                if (!this.IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1)
                                {
                                    break;
                                }

                                // Remove from the cache and delete each CachedImage.
                                CacheIndexer.Remove(fileInfo.Name);
                                fileInfo.Delete();
                                count -= 1;
                            }
                            // ReSharper disable once EmptyGeneralCatchClause
                            catch
                            {
                                // Log it but skip to the next file.
                                ImageProcessorBootstrapper.Instance.Logger.Log <DiskCache>("Unable to clean cached file: " + fileInfo.FullName);
                            }
                        }
                    }
                }
            }
        }
Exemple #8
0
        /// <summary>
        /// Trims a cached folder ensuring that it does not exceed the maximum file count.
        /// </summary>
        /// <param name="path">
        /// The path to the folder.
        /// </param>
        public static void TrimCachedFolders(string path)
        {
            string directory = Path.GetDirectoryName(path);

            if (directory != null)
            {
                DirectoryInfo directoryInfo       = new DirectoryInfo(directory);
                DirectoryInfo parentDirectoryInfo = directoryInfo.Parent;

                if (parentDirectoryInfo != null)
                {
                    // UNC folders can throw exceptions if the file doesn't exist.
                    foreach (DirectoryInfo enumerateDirectory in parentDirectoryInfo.SafeEnumerateDirectories())
                    {
                        IEnumerable <FileInfo> files = enumerateDirectory.EnumerateFiles().OrderBy(f => f.CreationTimeUtc);
                        int count = files.Count();

                        foreach (FileInfo fileInfo in files)
                        {
                            try
                            {
                                // If the group count is equal to the max count minus 1 then we know we
                                // have reduced the number of items below the maximum allowed.
                                // We'll cleanup any orphaned expired files though.
                                if (!IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1)
                                {
                                    break;
                                }

                                // Remove from the cache and delete each CachedImage.
                                CacheIndexer.Remove(fileInfo.Name);
                                fileInfo.Delete();
                                count -= 1;
                            }
                            // ReSharper disable once EmptyGeneralCatchClause
                            catch
                            {
                                // Do nothing; skip to the next file.
                            }
                        }
                    }
                }
            }
        }
Exemple #9
0
        /// <summary>
        /// Returns a value indicating whether the original file is new or has been updated.
        /// </summary>
        /// <param name="cachedPath">
        /// The path to the cached image.
        /// </param>
        /// <returns>
        /// True if The original file is new or has been updated; otherwise, false.
        /// </returns>
        public bool IsNewOrUpdatedFile(string cachedPath)
        {
            bool        isUpdated   = false;
            CachedImage cachedImage = CacheIndexer.GetValue(cachedPath);

            if (cachedImage == null)
            {
                // Nothing in the cache so we should return true.
                isUpdated = true;
            }
            else
            {
                // Check to see if the cached image is set to expire.
                if (IsExpired(cachedImage.CreationTimeUtc))
                {
                    CacheIndexer.Remove(cachedPath);
                    isUpdated = true;
                }
            }

            return(isUpdated);
        }
Exemple #10
0
        /// <summary>
        /// Trims a cached folder ensuring that it does not exceed the maximum file count.
        /// </summary>
        /// <param name="path">
        /// The path to the folder.
        /// </param>
        private void TrimCachedFolders(string path)
        {
            // ReSharper disable once AssignNullToNotNullAttribute
            DirectoryInfo directoryInfo       = new DirectoryInfo(Path.GetDirectoryName(path));
            DirectoryInfo parentDirectoryInfo = directoryInfo.Parent;

            // ReSharper disable once PossibleNullReferenceException
            foreach (DirectoryInfo enumerateDirectory in parentDirectoryInfo.EnumerateDirectories())
            {
                IEnumerable <FileInfo> files = enumerateDirectory.EnumerateFiles().OrderBy(f => f.CreationTimeUtc);
                int count = files.Count();

                foreach (FileInfo fileInfo in files)
                {
                    try
                    {
                        // If the group count is equal to the max count minus 1 then we know we
                        // have reduced the number of items below the maximum allowed.
                        // We'll cleanup any orphaned expired files though.
                        if (!this.IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1)
                        {
                            break;
                        }

                        // Remove from the cache and delete each CachedImage.
                        CacheIndexer.Remove(fileInfo.Name);
                        fileInfo.Delete();
                        count -= 1;
                    }
                    // ReSharper disable once EmptyGeneralCatchClause
                    catch
                    {
                        // Do nothing; skip to the next file.
                    }
                }
            }
        }
Exemple #11
0
        /// <summary>
        /// Trims the cache of any expired items in an asynchronous manner.
        /// </summary>
        /// <returns>
        /// The asynchronous <see cref="Task"/> representing an asynchronous operation.
        /// </returns>
        public override Task TrimCacheAsync()
        {
            if (!this.TrimCache)
            {
                return(Task.FromResult(0));
            }

            this.ScheduleCacheTrimmer(token =>
            {
                var rootDirectory = Path.GetDirectoryName(this.CachedPath);
                if (rootDirectory != null)
                {
                    // Jump up to the parent branch to clean through the cache
                    // UNC folders can throw exceptions if the file doesn't exist
                    var directories = SafeEnumerateDirectories(validatedAbsoluteCachePath).Reverse().ToList();
                    foreach (string directory in directories)
                    {
                        if (token.IsCancellationRequested)
                        {
                            break;
                        }

                        try
                        {
                            var files = Directory.EnumerateFiles(directory).Select(f => new FileInfo(f)).OrderBy(f => f.CreationTimeUtc).ToList();
                            var count = files.Count;
                            foreach (var fileInfo in files)
                            {
                                if (token.IsCancellationRequested)
                                {
                                    break;
                                }

                                try
                                {
                                    // If the group count is equal to the max count minus 1 then we know we have reduced the number of items below the maximum allowed
                                    // We'll cleanup any orphaned expired files though
                                    if (!this.IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1)
                                    {
                                        break;
                                    }

                                    // Remove from the cache and delete each CachedImage
                                    CacheIndexer.Remove(fileInfo.Name);
                                    fileInfo.Delete();
                                    count--;
                                }
                                catch (Exception ex)
                                {
                                    // Log it but skip to the next file
                                    ImageProcessorBootstrapper.Instance.Logger.Log <DiskCache>($"Unable to clean cached file: {fileInfo.FullName}, {ex.Message}");
                                }
                            }

                            // If the directory is empty, delete it to remove the FCN
                            if (count == 0 &&
                                Directory.GetDirectories(directory, "*", SearchOption.TopDirectoryOnly).Length == 0)
                            {
                                Directory.Delete(directory);
                            }
                        }
                        catch (Exception ex)
                        {
                            // Log it but skip to the next directory
                            ImageProcessorBootstrapper.Instance.Logger.Log <DiskCache>($"Unable to clean cached directory: {directory}, {ex.Message}");
                        }
                    }
                }

                return(Task.FromResult(0));
            });

            return(Task.FromResult(0));
        }
        /// <summary>
        /// Trims the cache of any expired items in an asynchronous manner.
        /// </summary>
        /// <returns>
        /// The asynchronous <see cref="Task"/> representing an asynchronous operation.
        /// </returns>
        public override Task TrimCacheAsync()
        {
            if (!this.TrimCache)
            {
                return(Task.FromResult(0));
            }

            this.ScheduleCacheTrimmer(token =>
            {
                string rootDirectory = Path.GetDirectoryName(this.CachedPath);

                if (rootDirectory != null)
                {
                    // Jump up to the parent branch to clean through the cache.
                    // UNC folders can throw exceptions if the file doesn't exist.
                    IEnumerable <string> directories = SafeEnumerateDirectories(validatedAbsoluteCachePath).Reverse();

                    foreach (string directory in directories)
                    {
                        if (!Directory.Exists(directory))
                        {
                            continue;
                        }

                        if (token.IsCancellationRequested)
                        {
                            break;
                        }

                        IEnumerable <FileInfo> files = Directory.EnumerateFiles(directory)
                                                       .Select(f => new FileInfo(f))
                                                       .OrderBy(f => f.CreationTimeUtc);
                        int count = files.Count();

                        foreach (FileInfo fileInfo in files)
                        {
                            if (token.IsCancellationRequested)
                            {
                                break;
                            }

                            try
                            {
                                // If the group count is equal to the max count minus 1 then we know we
                                // have reduced the number of items below the maximum allowed.
                                // We'll cleanup any orphaned expired files though.
                                if (!this.IsExpired(fileInfo.CreationTimeUtc) && count <= MaxFilesCount - 1)
                                {
                                    break;
                                }

                                // Remove from the cache and delete each CachedImage.
                                CacheIndexer.Remove(fileInfo.Name);
                                fileInfo.Delete();
                                count--;
                            }
                            catch (Exception ex)
                            {
                                // Log it but skip to the next file.
                                ImageProcessorBootstrapper.Instance.Logger.Log <DiskCache>($"Unable to clean cached file: {fileInfo.FullName}, {ex.Message}");
                            }
                        }

                        // If the directory is empty of files delete it to remove the FCN.
                        this.RecursivelyDeleteEmptyDirectories(directory, validatedAbsoluteCachePath, token);
                    }
                }
                return(Task.FromResult(0));
            });

            return(Task.FromResult(0));
        }