public async Task <T> GetAsync <T>(string key, Func <Task <T> > acquirer, TimeSpan?duration = null, bool independent = false)
        {
            if (TryGet(key, independent, out T value))
            {
                return(value);
            }

            if (_scopeAccessor.Value.HasScope(key))
            {
                throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key));
            }

            // Get the async (semaphore) locker specific to this key
            using (await KeyedLock.LockAsync("cache:" + key, TimeSpan.FromMinutes(1)))
            {
                if (!TryGet(key, independent, out value))
                {
                    using (_scopeAccessor.Value.BeginScope(key))
                    {
                        value = await acquirer();

                        Put(key, value, duration, _scopeAccessor.Value.Current.Dependencies);
                        return(value);
                    }
                }
            }

            return(value);
        }
        public async Task <T> GetAsync <T>(string key, Func <Task <T> > acquirer, TimeSpan?duration = null, bool independent = false, bool allowRecursion = false)
        {
            if (TryGet(key, independent, out T value))
            {
                return(value);
            }

            if (!allowRecursion && _scopeAccessor.Value.HasScope(key))
            {
                throw new LockRecursionException(LockRecursionExceptionMessage.FormatInvariant(key));
            }

            // Get the async (semaphore) locker specific to this key
            using (await KeyedLock.LockAsync("cache:" + key, TimeSpan.FromMinutes(1)))
            {
                if (!TryGet(key, independent, out value))
                {
                    var scope = !allowRecursion?_scopeAccessor.Value.BeginScope(key) : ActionDisposable.Empty;

                    using (scope)
                    {
                        value = await acquirer();

                        var dependencies = !allowRecursion ? _scopeAccessor.Value.Current?.Dependencies : (IEnumerable <string>)null;
                        Put(key, value, duration, dependencies);
                        return(value);
                    }
                }
            }

            return(value);
        }
Exemple #3
0
        private async Task <ActionResult> HandleImageAsync(
            ProcessImageQuery query,
            CachedImageResult cachedImage,
            string nameWithoutExtension,
            string mime,
            string extension,
            Func <string, Task <byte[]> > getSourceBufferAsync)
        {
            string prevMime = null;

            if (extension != cachedImage.Extension)
            {
                // The query requests another format.
                // Adjust extension and mime type fo proper ETag creation.
                extension = cachedImage.Extension;
                prevMime  = mime;
                mime      = MimeTypes.MapNameToMimeType(cachedImage.FileName);
            }

            try
            {
                if (!cachedImage.Exists)
                {
                    // Lock concurrent requests to same resource
                    using (await KeyedLock.LockAsync("MediaController.HandleImage." + cachedImage.Path))
                    {
                        _imageCache.RefreshInfo(cachedImage);

                        // File could have been processed by another request in the meantime, check again.
                        if (!cachedImage.Exists)
                        {
                            // Call inner function
                            byte[] source = await getSourceBufferAsync(prevMime);

                            if (source == null || source.Length == 0)
                            {
                                return(NotFound(mime));
                            }

                            source = await ProcessAndPutToCacheAsync(cachedImage, source, query);

                            return(new CachedFileResult(mime, cachedImage.LastModifiedUtc.GetValueOrDefault(), () => source, source.LongLength));
                        }
                    }
                }

                if (Request.HttpMethod == "HEAD")
                {
                    return(new HttpStatusCodeResult(200));
                }

                if (cachedImage.IsRemote && !_streamRemoteMedia)
                {
                    // Redirect to existing remote file
                    Response.ContentType = mime;
                    return(Redirect(_imageCache.GetPublicUrl(cachedImage.Path)));
                }
                else
                {
                    // Open existing stream
                    return(new CachedFileResult(cachedImage.File, mime));
                }
            }
            catch (Exception ex)
            {
                if (!(ex is ProcessImageException))
                {
                    // ProcessImageException is logged already in ImageProcessor
                    Logger.ErrorFormat(ex, "Error processing media file '{0}'.", cachedImage.Path);
                }
                return(new HttpStatusCodeResult(500, ex.Message));
            }
        }
        public async Task ExecuteAsync(MediaHandlerContext context)
        {
            if (!IsProcessable(context))
            {
                return;
            }

            var query    = context.ImageQuery;
            var pathData = context.PathData;

            var cachedImage = ImageCache.Get(context.MediaFileId, pathData, query);

            if (!pathData.Extension.IsCaseInsensitiveEqual(cachedImage.Extension))
            {
                // The query requests another format.
                // Adjust extension and mime type fo proper ETag creation.
                pathData.Extension = cachedImage.Extension;
                pathData.MimeType  = cachedImage.MimeType;
            }

            var exists = cachedImage.Exists;

            if (exists && cachedImage.FileSize == 0)
            {
                // Empty file means: thumb extraction failed before and will most likely fail again.
                // Don't bother proceeding.
                context.Exception = ExceptionFactory.ExtractThumbnail(cachedImage.FileName);
                context.Executed  = true;
                return;
            }

            if (!exists)
            {
                // Lock concurrent requests to same resource
                using (await KeyedLock.LockAsync("ImageHandlerBase.Execute." + cachedImage.Path))
                {
                    ImageCache.RefreshInfo(cachedImage);

                    // File could have been processed by another request in the meantime, check again.
                    if (!cachedImage.Exists)
                    {
                        // Call inner function
                        var sourceFile = context.SourceFile;
                        if (sourceFile == null || sourceFile.Size == 0)
                        {
                            context.Executed = true;
                            return;
                        }

                        var inputStream = sourceFile.OpenRead();
                        if (inputStream == null)
                        {
                            context.Exception = ExceptionFactory.ExtractThumbnail(sourceFile.Path, T("Admin.Media.Exception.NullInputStream"));
                            context.Executed  = true;
                            return;
                        }

                        try
                        {
                            await ProcessImageAsync(context, cachedImage, inputStream);
                        }
                        catch (Exception ex)
                        {
                            Logger.Error(ex);

                            if (ex is ExtractThumbnailException)
                            {
                                // Thumbnail extraction failed and we must assume that it always will fail.
                                // Therefore we create an empty file to prevent repetitive processing.
                                using (var memStream = new MemoryStream())
                                {
                                    await ImageCache.PutAsync(cachedImage, memStream);
                                }
                            }

                            context.Exception = ex;
                            context.Executed  = true;
                            return;
                        }
                        finally
                        {
                            if (inputStream != null)
                            {
                                inputStream.Dispose();
                            }
                        }

                        if (context.ResultImage != null)
                        {
                            ImageCache.Put(cachedImage, context.ResultImage);
                            context.ResultFile = cachedImage.File;
                        }

                        context.Executed = true;
                        return;
                    }
                }
            }

            // Cached image existed already
            context.ResultFile = cachedImage.File;
            context.Executed   = true;
        }