Example #1
0
        public static async Task CreateCacheFileAsync(
            HttpCacheResult result,
            string uri,
            HttpResponseMessage response,
            HttpSourceCacheContext context,
            Action <Stream> ensureValidContents,
            CancellationToken cancellationToken)
        {
            // The update of a cached file is divided into two steps:
            // 1) Delete the old file.
            // 2) Create a new file with the same name.
            using (var fileStream = new FileStream(
                       result.NewCacheFile,
                       FileMode.Create,
                       FileAccess.ReadWrite,
                       FileShare.None,
                       BufferSize,
                       useAsync: true))
            {
                using (var networkStream = await response.Content.ReadAsStreamAsync())
                {
                    await networkStream.CopyToAsync(fileStream, BufferSize, cancellationToken);
                }

                // Validate the content before putting it into the cache.
                fileStream.Seek(0, SeekOrigin.Begin);
                ensureValidContents?.Invoke(fileStream);
            }

            if (File.Exists(result.CacheFile))
            {
                // Process B can perform deletion on an opened file if the file is opened by process A
                // with FileShare.Delete flag. However, the file won't be actually deleted until A close it.
                // This special feature can cause race condition, so we never delete an opened file.
                if (!IsFileAlreadyOpen(result.CacheFile))
                {
                    File.Delete(result.CacheFile);
                }
            }

            // If the destination file doesn't exist, we can safely perform moving operation.
            // Otherwise, moving operation will fail.
            if (!File.Exists(result.CacheFile))
            {
                File.Move(
                    result.NewCacheFile,
                    result.CacheFile);
            }

            // Even the file deletion operation above succeeds but the file is not actually deleted,
            // we can still safely read it because it means that some other process just updated it
            // and we don't need to update it with the same content again.
            result.Stream = new FileStream(
                result.CacheFile,
                FileMode.Open,
                FileAccess.Read,
                FileShare.Read | FileShare.Delete,
                BufferSize,
                useAsync: true);
        }
        public static async Task CreateCacheFileAsync(
            HttpCacheResult result,
            HttpResponseMessage response,
            Action <Stream> ensureValidContents,
            CancellationToken cancellationToken)
        {
            // Get the cache file directories, so we can make sure they are created before writing
            // files to them.
            var newCacheFileDirectory = Path.GetDirectoryName(result.NewFile);
            var cacheFileDirectory    = Path.GetDirectoryName(result.CacheFile);

            // Make sure the new cache file directory is created before writing a file to it.
            DirectoryUtility.CreateSharedDirectory(newCacheFileDirectory);

            // The update of a cached file is divided into two steps:
            // 1) Delete the old file.
            // 2) Create a new file with the same name.
            using (var fileStream = new FileStream(
                       result.NewFile,
                       FileMode.Create,
                       FileAccess.ReadWrite,
                       FileShare.None,
                       BufferSize))
            {
                using (var networkStream = await response.Content.ReadAsStreamAsync())
                {
                    await networkStream.CopyToAsync(fileStream, BufferSize, cancellationToken);
                }

                // Validate the content before putting it into the cache.
                if (ensureValidContents != null)
                {
                    fileStream.Seek(0, SeekOrigin.Begin);
                    ensureValidContents.Invoke(fileStream);
                }
            }

            if (File.Exists(result.CacheFile))
            {
                // Process B can perform deletion on an opened file if the file is opened by process A
                // with FileShare.Delete flag. However, the file won't be actually deleted until A close it.
                // This special feature can cause race condition, so we never delete an opened file.
                if (!CachingUtility.IsFileAlreadyOpen(result.CacheFile))
                {
                    File.Delete(result.CacheFile);
                }
            }

            // Make sure the cache file directory is created before moving or writing a file to it.
            if (cacheFileDirectory != newCacheFileDirectory)
            {
                DirectoryUtility.CreateSharedDirectory(cacheFileDirectory);
            }

            // If the destination file doesn't exist, we can safely perform moving operation.
            // Otherwise, moving operation will fail.
            if (!File.Exists(result.CacheFile))
            {
                File.Move(
                    result.NewFile,
                    result.CacheFile);
            }

            // Even the file deletion operation above succeeds but the file is not actually deleted,
            // we can still safely read it because it means that some other process just updated it
            // and we don't need to update it with the same content again.
            result.Stream = new FileStream(
                result.CacheFile,
                FileMode.Open,
                FileAccess.Read,
                FileShare.Read | FileShare.Delete,
                BufferSize);
        }
        public static async Task CreateCacheFileAsync(
            HttpCacheResult result,
            HttpResponseMessage response,
            Action <Stream> ensureValidContents,
            CancellationToken cancellationToken)
        {
            // Get the cache file directories, so we can make sure they are created before writing
            // files to them.
            var newCacheFileDirectory = Path.GetDirectoryName(result.NewFile);
            var cacheFileDirectory    = Path.GetDirectoryName(result.CacheFile);

            // Make sure the new cache file directory is created before writing a file to it.
            DirectoryUtility.CreateSharedDirectory(newCacheFileDirectory);

            // The update of a cached file is divided into two steps:
            // 1) Delete the old file.
            // 2) Create a new file with the same name.

            // Some FileStream operations on Windows are synchronous even though it may not seem so.
            // The HTTP stack rewrite in .NET Core 2.1 introduced circumstances whereby these
            // synchronous FileStream calls will keep an IO completion thread busy and block other
            // HTTP requests from completing.  The immediate solution is to perform write and read
            // operations on separate streams, but only on .NET Core where the problem exists.
            // See https://github.com/dotnet/corefx/issues/31914 for details.
            const int writeBufferSize =
#if IS_CORECLR
                1;  // This disables write buffering.
#else
                BufferSize;
#endif

            using (var fileStream = new FileStream(
                       result.NewFile,
                       FileMode.Create,
                       FileAccess.Write,
                       FileShare.None,
                       writeBufferSize,
                       useAsync: true))
            {
                using (var networkStream = await response.Content.ReadAsStreamAsync())
                {
                    await networkStream.CopyToAsync(fileStream, BufferSize, cancellationToken);
                }
            }

            using (var fileStream = new FileStream(
                       result.NewFile,
                       FileMode.Open,
                       FileAccess.Read,
                       FileShare.None,
                       BufferSize,
                       useAsync: true))
            {
                // Validate the content before putting it into the cache.
                ensureValidContents?.Invoke(fileStream);
            }

            if (File.Exists(result.CacheFile))
            {
                // Process B can perform deletion on an opened file if the file is opened by process A
                // with FileShare.Delete flag. However, the file won't be actually deleted until A close it.
                // This special feature can cause race condition, so we never delete an opened file.
                if (!CachingUtility.IsFileAlreadyOpen(result.CacheFile))
                {
                    File.Delete(result.CacheFile);
                }
            }

            // Make sure the cache file directory is created before moving or writing a file to it.
            if (cacheFileDirectory != newCacheFileDirectory)
            {
                DirectoryUtility.CreateSharedDirectory(cacheFileDirectory);
            }

            // If the destination file doesn't exist, we can safely perform moving operation.
            // Otherwise, moving operation will fail.
            if (!File.Exists(result.CacheFile))
            {
                File.Move(
                    result.NewFile,
                    result.CacheFile);
            }

            // Even the file deletion operation above succeeds but the file is not actually deleted,
            // we can still safely read it because it means that some other process just updated it
            // and we don't need to update it with the same content again.
            result.Stream = new FileStream(
                result.CacheFile,
                FileMode.Open,
                FileAccess.Read,
                FileShare.Read | FileShare.Delete,
                BufferSize,
                useAsync: true);
        }