예제 #1
0
        async Task <List <AttachmentInfo> > ReceiveByFormFileAsync(HttpContext context, string serviceName, string objectName, string systemID, string entityInfo, string objectID, bool isShared, bool isTracked, bool isTemporary, CancellationToken cancellationToken)
        {
            var attachments = new List <AttachmentInfo>();
            await context.Request.Form.Files.Where(file => file != null && file.Length > 0).ForEachAsync(async(file, token) =>
            {
                using (var uploadStream = file.OpenReadStream())
                {
                    // prepare
                    var attachment = new AttachmentInfo
                    {
                        ID          = context.GetParameter("x-attachment-id") ?? UtilityService.NewUUID,
                        ServiceName = serviceName,
                        ObjectName  = objectName,
                        SystemID    = systemID,
                        EntityInfo  = entityInfo,
                        ObjectID    = objectID,
                        Size        = file.Length,
                        Filename    = file.FileName,
                        ContentType = file.ContentType,
                        IsShared    = isShared,
                        IsTracked   = isTracked,
                        IsTemporary = isTemporary,
                        Title       = file.FileName,
                        Description = "",
                        IsThumbnail = false
                    };

                    // save file into disc
                    using (var fileStream = new FileStream(attachment.GetFilePath(true), FileMode.Create, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete, AspNetCoreUtilityService.BufferSize, true))
                    {
                        var buffer = new byte[AspNetCoreUtilityService.BufferSize];
                        var read   = 0;
                        do
                        {
                            read = await uploadStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);
                            if (read > 0)
                            {
                                await fileStream.WriteAsync(buffer, 0, read, cancellationToken).ConfigureAwait(false);
                                await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);
                            }
                        } while (read > 0);
                    }

                    // update attachment info
                    attachments.Add(attachment);
                }
            }, cancellationToken, true, false).ConfigureAwait(false);

            return(attachments);
        }
예제 #2
0
        async Task ReceiveAsync(HttpContext context, CancellationToken cancellationToken)
        {
            // prepare
            var stopwatch   = Stopwatch.StartNew();
            var serviceName = context.GetParameter("x-service-name");
            var objectName  = context.GetParameter("x-object-name");
            var systemID    = context.GetParameter("x-system-id");
            var entityInfo  = context.GetParameter("x-entity");
            var objectID    = context.GetParameter("x-object-id");
            var isTemporary = "true".IsEquals(context.GetParameter("x-temporary"));

            if (string.IsNullOrWhiteSpace(objectID))
            {
                throw new InvalidRequestException("Invalid object identity");
            }

            // check permissions
            var gotRights = isTemporary
                                ? await context.CanContributeAsync(serviceName, objectName, systemID, entityInfo, "", cancellationToken).ConfigureAwait(false)
                                : await context.CanEditAsync(serviceName, objectName, systemID, entityInfo, objectID, cancellationToken).ConfigureAwait(false);

            if (!gotRights)
            {
                throw new AccessDeniedException();
            }

            // limit size - default is 512 KB
            if (!Int32.TryParse(UtilityService.GetAppSetting("Limits:Thumbnail"), out var limitSize))
            {
                limitSize = 512;
            }

            // read upload file
            var thumbnails = new List <byte[]>();
            var asBase64   = context.GetParameter("x-as-base64") != null;

            if (asBase64)
            {
                var base64Data = (await context.ReadTextAsync(cancellationToken).ConfigureAwait(false)).ToJson()["Data"];
                if (base64Data is JArray)
                {
                    (base64Data as JArray).Take(7).ForEach(data =>
                    {
                        var thumbnail = (data as JValue).Value.ToString().ToArray().Last().Base64ToBytes();
                        if (thumbnail != null && thumbnail.Length <= limitSize * 1024)
                        {
                            thumbnails.Add(thumbnail);
                        }
                        else
                        {
                            thumbnails.Add(null);
                        }
                    });
                }
                else
                {
                    thumbnails.Add((base64Data as JValue).Value.ToString().ToArray().Last().Base64ToBytes());
                }
            }
            else
            {
                for (var index = 0; index < context.Request.Form.Files.Count && index < 7; index++)
                {
                    thumbnails.Add(null);
                }
                await context.Request.Form.Files.Take(7).ForEachAsync(async(file, index, _) =>
                {
                    if (file != null && file.ContentType.IsStartsWith("image/") && file.Length > 0 && file.Length <= limitSize * 1024)
                    {
                        using (var stream = file.OpenReadStream())
                        {
                            var thumbnail = new byte[file.Length];
                            await stream.ReadAsync(thumbnail, 0, (int)file.Length, cancellationToken).ConfigureAwait(false);
                            thumbnails[index] = thumbnail;
                        }
                    }
                }, cancellationToken, true, false).ConfigureAwait(false);
            }

            // save uploaded files & create meta info
            var attachments = new List <AttachmentInfo>();
            var useCache    = "true".IsEquals(UtilityService.GetAppSetting("Files:Cache:Thumbnails", "true")) && Global.Cache != null;

            try
            {
                // save uploaded files into disc
                var title = "";
                try
                {
                    title = context.GetParameter("x-object-title")?.Url64Decode()?.GetANSIUri() ?? UtilityService.NewUUID;
                }
                catch
                {
                    title = UtilityService.NewUUID;
                }
                await thumbnails.ForEachAsync(async (thumbnail, index, _) =>
                {
                    if (thumbnail != null)
                    {
                        // prepare
                        var attachment = new AttachmentInfo
                        {
                            ID          = context.GetParameter("x-attachment-id") ?? UtilityService.NewUUID,
                            ServiceName = serviceName,
                            ObjectName  = objectName,
                            SystemID    = systemID,
                            EntityInfo  = entityInfo,
                            ObjectID    = objectID,
                            Size        = thumbnail.Length,
                            Filename    = $"{objectID}{(index > 0 ? $"-{index}" : "")}.jpg",
                            ContentType = "image/jpeg",
                            IsShared    = false,
                            IsTracked   = false,
                            IsTemporary = isTemporary,
                            Title       = title,
                            Description = "",
                            IsThumbnail = true
                        };

                        // save file into temporary directory
                        await UtilityService.WriteBinaryFileAsync(attachment.GetFilePath(true), thumbnail, false, cancellationToken).ConfigureAwait(false);

                        // update attachment info
                        attachments.Add(attachment);
                    }
                }, cancellationToken, true, false).ConfigureAwait(false);

                // create meta info
                var response  = new JArray();
                var cacheKeys = new List <string>();
                await attachments.ForEachAsync(async (attachment, token) =>
                {
                    response.Add(await context.CreateAsync(attachment, token).ConfigureAwait(false));
                    if (useCache)
                    {
                        var keys = await Global.Cache.GetSetMembersAsync($"{attachment.ObjectID}:Thumbnails", cancellationToken).ConfigureAwait(false);
                        if (keys != null && keys.Count > 0)
                        {
                            cacheKeys = cacheKeys.Concat(new[] { $"{attachment.ObjectID}:Thumbnails" }).Concat(keys).ToList();
                        }
                    }
                }, cancellationToken, true, false).ConfigureAwait(false);

                // clear cache
                if (useCache && cacheKeys.Count > 0)
                {
                    await Global.Cache.RemoveAsync(cacheKeys.Distinct(StringComparer.OrdinalIgnoreCase).ToList(), cancellationToken).ConfigureAwait(false);
                }

                // move files from temporary directory to official directory
                attachments.ForEach(attachment => attachment.PrepareDirectories().MoveFile(this.Logger, "Http.Uploads", true));

                // response
                await context.WriteAsync(response, cancellationToken).ConfigureAwait(false);

                stopwatch.Stop();
                if (Global.IsDebugLogEnabled)
                {
                    await context.WriteLogsAsync(this.Logger, "Http.Uploads", $"{thumbnails.Count(thumbnail => thumbnail != null)} thumbnail image(s) has been uploaded - Mode:  {(asBase64 ? "base64" : "file")} - Execution times: {stopwatch.GetElapsedTimes()}").ConfigureAwait(false);
                }
            }
            catch (Exception)
            {
                attachments.ForEach(attachment => attachment.DeleteFile(true, this.Logger, "Http.Uploads"));
                throw;
            }
        }
예제 #3
0
        async Task <List <AttachmentInfo> > ReceiveByFormDataAsync(HttpContext context, string serviceName, string objectName, string systemID, string entityInfo, string objectID, bool isShared, bool isTracked, bool isTemporary, CancellationToken cancellationToken)
        {
            // check
            var attachments = new List <AttachmentInfo>();

            if (string.IsNullOrWhiteSpace(context.Request.ContentType) || context.Request.ContentType.PositionOf("multipart/") < 0)
            {
                return(attachments);
            }

            // prepare the reader
            var boundary = context.Request.ContentType.ToArray(' ').Where(entry => entry.StartsWith("boundary=")).First().Substring(9);

            if (boundary.Length >= 2 && boundary[0] == '"' && boundary[boundary.Length - 1] == '"')
            {
                boundary = boundary.Substring(1, boundary.Length - 2);
            }
            var reader = new MultipartReader(boundary, context.Request.Body);

            // save all files into temporary directory
            MultipartSection section = null;

            do
            {
                // read the section
                section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false);

                if (section == null)
                {
                    break;
                }

                // prepare filename
                var filename = "";
                try
                {
                    filename = section.ContentDisposition.ToArray(';').First(part => part.Contains("filename")).ToArray('=').Last().Trim('"');
                }
                catch { }
                if (string.IsNullOrWhiteSpace(filename))
                {
                    continue;
                }

                // prepare info
                var attachment = new AttachmentInfo
                {
                    ID          = context.GetParameter("x-attachment-id") ?? UtilityService.NewUUID,
                    ServiceName = serviceName,
                    ObjectName  = objectName,
                    SystemID    = systemID,
                    EntityInfo  = entityInfo,
                    ObjectID    = objectID,
                    Filename    = filename,
                    ContentType = section.ContentType,
                    IsShared    = isShared,
                    IsTracked   = isTracked,
                    IsTemporary = isTemporary,
                    Title       = filename,
                    Description = "",
                    IsThumbnail = false
                };

                // save file into disc
                using (var fileStream = new FileStream(attachment.GetFilePath(true), FileMode.Create, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete, AspNetCoreUtilityService.BufferSize, true))
                {
                    var buffer = new byte[AspNetCoreUtilityService.BufferSize];
                    var read   = 0;
                    do
                    {
                        read = await section.Body.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false);

                        if (read > 0)
                        {
                            await fileStream.WriteAsync(buffer, 0, read, cancellationToken).ConfigureAwait(false);

                            await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);
                        }
                    } while (read > 0);
                    attachment.Size = fileStream.Length;
                }

                // update attachment info
                attachments.Add(attachment);
            } while (section == null);

            // return info of all uploaded files
            return(attachments);
        }
예제 #4
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);
        }