Ejemplo n.º 1
0
        async Task FlushAsync(HttpContext context, CancellationToken cancellationToken)
        {
            // prepare
            var requestUri   = context.GetRequestUri();
            var pathSegments = requestUri.GetRequestPathSegments();

            var attachment = new AttachmentInfo
            {
                ID          = pathSegments.Length > 3 && pathSegments[3].IsValidUUID() ? pathSegments[3].ToLower() : "",
                ServiceName = pathSegments.Length > 1 && !pathSegments[1].IsValidUUID() ? pathSegments[1] : "",
                SystemID    = pathSegments.Length > 1 && pathSegments[1].IsValidUUID() ? pathSegments[1].ToLower() : "",
                ContentType = pathSegments.Length > 2 ? pathSegments[2].Replace("=", "/") : "",
                Filename    = pathSegments.Length > 4 && pathSegments[3].IsValidUUID() ? pathSegments[4].UrlDecode() : "",
                IsThumbnail = false
            };

            if (string.IsNullOrWhiteSpace(attachment.ID) || string.IsNullOrWhiteSpace(attachment.Filename))
            {
                throw new InvalidRequestException();
            }

            // check "If-Modified-Since" request to reduce traffict
            var eTag          = "file#" + attachment.ID;
            var noneMatch     = context.GetHeaderParameter("If-None-Match");
            var modifiedSince = context.GetHeaderParameter("If-Modified-Since") ?? context.GetHeaderParameter("If-Unmodified-Since");

            if (eTag.IsEquals(noneMatch) && modifiedSince != null)
            {
                context.SetResponseHeaders((int)HttpStatusCode.NotModified, eTag, modifiedSince.FromHttpDateTime().ToUnixTimestamp(), "public", context.GetCorrelationID());
                await context.FlushAsync(cancellationToken).ConfigureAwait(false);

                if (Global.IsDebugLogEnabled)
                {
                    context.WriteLogs(this.Logger, "Http.Downloads", $"Response to request with status code 304 to reduce traffic ({requestUri})");
                }
                return;
            }

            // get info & check permissions
            attachment = await context.GetAsync(attachment.ID, cancellationToken).ConfigureAwait(false);

            if (!await context.CanDownloadAsync(attachment, cancellationToken).ConfigureAwait(false))
            {
                throw new AccessDeniedException();
            }

            // check existed
            var cacheKey = attachment.ContentType.IsStartsWith("image/") && "true".IsEquals(UtilityService.GetAppSetting("Files:Cache:Images", "true")) && Global.Cache != null
                                ? $"Image#{attachment.Filename.ToLower().GenerateUUID()}"
                                : null;
            var hasCached = cacheKey != null && await Global.Cache.ExistsAsync(cacheKey, cancellationToken).ConfigureAwait(false);

            FileInfo fileInfo = null;

            if (!hasCached)
            {
                fileInfo = new FileInfo(attachment.GetFilePath());
                if (!fileInfo.Exists)
                {
                    if (Global.IsDebugLogEnabled)
                    {
                        context.WriteLogs(this.Logger, "Http.Downloads", $"Not found: {requestUri} => {fileInfo.FullName}");
                    }
                    context.ShowHttpError((int)HttpStatusCode.NotFound, "Not Found", "FileNotFoundException", context.GetCorrelationID());
                    return;
                }
            }

            // flush the file to output stream, update counter & logs
            if (hasCached)
            {
                var lastModified = await Global.Cache.GetAsync <long>($"{cacheKey}:time", cancellationToken).ConfigureAwait(false);

                var bytes = await Global.Cache.GetAsync <byte[]>(cacheKey, cancellationToken).ConfigureAwait(false);

                using (var stream = UtilityService.CreateMemoryStream(bytes))
                {
                    await context.WriteAsync(stream, attachment.ContentType, attachment.IsReadable()?null : attachment.Filename, eTag, lastModified, "public", TimeSpan.FromDays(366), null, context.GetCorrelationID(), cancellationToken).ConfigureAwait(false);
                }
                if (Global.IsDebugLogEnabled)
                {
                    context.WriteLogs(this.Logger, "Http.Downloads", $"Successfully flush a cached image ({requestUri})");
                }
            }
            else
            {
                await context.WriteAsync(fileInfo, attachment.IsReadable()?null : attachment.Filename, eTag, cancellationToken).ConfigureAwait(false);

                if (cacheKey != null)
                {
                    await Task.WhenAll
                    (
                        Global.Cache.SetAsFragmentsAsync(cacheKey, await UtilityService.ReadBinaryFileAsync(fileInfo, cancellationToken).ConfigureAwait(false), 1440, cancellationToken),
                        Global.Cache.SetAsync($"{cacheKey}:time", fileInfo.LastWriteTime.ToUnixTimestamp(), 1440, cancellationToken),
                        Global.IsDebugLogEnabled?context.WriteLogsAsync(this.Logger, "Http.Downloads", $"Update an image file into cache successful ({requestUri})") : Task.CompletedTask
                    ).ConfigureAwait(false);
                }
                if (Global.IsDebugLogEnabled)
                {
                    context.WriteLogs(this.Logger, "Http.Downloads", $"Successfully flush a file [{requestUri} => {fileInfo.FullName}]");
                }
            }

            await context.UpdateAsync(attachment, attachment.IsReadable()? "Direct" : "Download", cancellationToken).ConfigureAwait(false);
        }