Example #1
0
        protected string GetPath(long id, GetFileModify modify = null)//int size = 0)
        {
            var name = id.ToString();

            if (modify != null)
            {
                var extraFolder = "_";

                if (modify.size > 0)
                {
                    extraFolder += $"{modify.size}";
                }
                if (modify.crop)
                {
                    extraFolder += "a";
                }

                if (extraFolder != "_")
                {
                    return(Path.Join(config.Location, extraFolder, name));
                }
            }

            return(Path.Join(config.Location, name));
        }
    public async Task <byte[]> GetThumbnailAsync(string hash, string thumbnailPath, GetFileModify modify)
    {
        //NOTE: locking on the entire write may increase wait times for brand new images (and image uploads) but it saves cpu cycles
        //in those cases by only resizing an image once.
        await filelock.WaitAsync();

        try
        {
            //Will checking the fileinfo be too much??
            if (!System.IO.File.Exists(thumbnailPath) || (new FileInfo(thumbnailPath)).Length == 0)
            {
                Directory.CreateDirectory(Path.GetDirectoryName(thumbnailPath) ?? throw new InvalidOperationException("No parent for thumbail path?"));

                using (var memStream = new MemoryStream(await GetMainDataAsync(hash)))
                {
                    var manipResult = await imageManip.MakeThumbnailAndSave(memStream, thumbnailPath, modify);

                    AddImageRender(manipResult.RenderCount + manipResult.LoadCount);
                }
            }
        }
        finally
        {
            filelock.Release();
        }

        return(await System.IO.File.ReadAllBytesAsync(thumbnailPath));
    }
Example #3
0
        protected string GetAndMakePath(long id, GetFileModify modify = null)
        {
            var result = GetPath(id, modify);

            System.IO.Directory.CreateDirectory(Path.GetDirectoryName(result));
            return(result);
        }
    public async Task <ActionResult <bool> > GetFileAsync([FromRoute] string hash, [FromQuery] GetFileModify modify)
    {
        //This is fine because files contents will never change, BUT isn't appropriate for most other things!
        Response.Headers.Add("ETag", hash);

        try
        {
            var result = await service.GetFileAsync(hash, modify);

            return(File(result.Item1, result.Item2));
        }
        catch (Exception ex)
        {
            return(await MatchExceptions(() =>
            {
                ExceptionDispatchInfo.Capture(ex).Throw();
                return Task.FromResult(true);
            }));
        }
    }
    public async Task <Tuple <byte[], string> > GetFileAsync(string hash, GetFileModify modify)
    {
        if (modify.size > 0)
        {
            if (modify.size < 10)
            {
                throw new RequestException("Requested file size too small!");
            }
            else if (modify.size <= 100)
            {
                modify.size = 10 * (modify.size / 10);
            }
            else if (modify.size <= 1000)
            {
                modify.size = 100 * (modify.size / 100);
            }
            else
            {
                throw new RequestException("Requested file size too large!");
            }
        }

        //Go get that ONE file. This should return null if we can't read it... let's hope!
        //SPECIAL: the 0 file is
        ContentView?fileData = null;

        if (hash == Constants.DefaultHash)
        {
            fileData = new ContentView()
            {
                id = 0, literalType = FallbackMime, hash = hash, contentType = InternalContentType.file
            };
        }
        else
        {
            //Doesn't matter who the requester is, ANY file with this hash is fine... what about deleted though?
            using var searcher = dbFactory.CreateSearch();
            fileData           = (await searcher.GetByField <ContentView>(RequestType.content, "hash", hash)).FirstOrDefault();
        }

        if (fileData == null || fileData.deleted || fileData.contentType != InternalContentType.file)
        {
            throw new NotFoundException($"Couldn't find file data with hash {hash}");
        }

        var thumbnailPath = GetThumbnailPath(hash, modify);
        var mimeType      = fileData.literalType ?? "";

        try
        {
            //This means they're requesting a MAIN data, just go get it
            if (string.IsNullOrEmpty(thumbnailPath))
            {
                return(Tuple.Create(await GetMainDataAsync(hash), mimeType));
            }
            else
            {
                return(Tuple.Create(await GetThumbnailAsync(hash, thumbnailPath, modify), mimeType));
            }
        }
        catch (NotFoundException)
        {
            //This just means we CAN generate it!
            if (hash == Constants.DefaultHash && !string.IsNullOrWhiteSpace(config.DefaultImageFallback))
            {
                logger.LogInformation($"Creating default image {hash} from base64 string given in config");
                await SaveMainDataAsync(Convert.FromBase64String(config.DefaultImageFallback), hash, mimeType);

                return(await GetFileAsync(hash, modify));
            }
            else
            {
                throw;
            }
        }
    }
Example #6
0
        [ResponseCache(Duration = 13824000)] //six months
        public async Task <IActionResult> GetFileAsync([FromRoute] long id, [FromQuery] GetFileModify modify)
        {
            if (modify.size > 0)
            {
                if (modify.size < 10)
                {
                    return(BadRequest("Too small!"));
                }
                else if (modify.size <= 100)
                {
                    modify.size = 10 * (modify.size / 10);
                }
                else if (modify.size <= 1000)
                {
                    modify.size = 100 * (modify.size / 100);
                }
                else
                {
                    return(BadRequest("Too large!"));
                }
            }

            var requester = GetRequesterNoFail();

            //Go get that ONE file. This should return null if we can't read it... let's hope!
            //SPECIAL: the 0 file is
            FileView fileData = null;

            if (id == 0)
            {
                if (System.IO.File.Exists(GetAndMakePath(0)))
                {
                    fileData = new FileView()
                    {
                        id = 0, fileType = "image/png"
                    }
                }
                ;
            }
            else
            {
                fileData = await service.FindByIdAsync(id, requester);
            }

            if (fileData == null)
            {
                return(NotFound());
            }

            var finalPath = GetAndMakePath(fileData.id, modify);

            //Ok NOW we can go get it. We may need to perform a resize beforehand if we can't find the file.
            if (!System.IO.File.Exists(finalPath))
            {
                var          baseImage = GetPath(fileData.id);
                IImageFormat format;

                await Task.Run(() =>
                {
                    using (var image = Image.Load(baseImage, out format))
                    {
                        var maxDim = Math.Max(image.Width, image.Height);
                        var minDim = Math.Min(image.Width, image.Height);

                        //Square ALWAYS happens, it can happen before other things.
                        if (modify.crop)
                        {
                            image.Mutate(x => x.Crop(new Rectangle((image.Width - minDim) / 2, (image.Height - minDim) / 2, minDim, minDim)));
                        }

                        if (modify.size > 0 && !((format.DefaultMimeType == "image/gif" /*|| modify.noGrow*/) && (modify.size > image.Width || modify.size > image.Height)))
                        {
                            var width  = 0;
                            var height = 0;

                            //Preserve aspect ratio when not square
                            if (image.Width > image.Height)
                            {
                                width = modify.size;
                            }
                            else
                            {
                                height = modify.size;
                            }

                            image.Mutate(x => x.Resize(width, height));
                        }

                        using (var stream = System.IO.File.OpenWrite(finalPath))
                        {
                            image.Save(stream, format);
                        }
                    }
                });
            }

            Response.Headers.Add("ETag", GetETag(finalPath));
            return(File(System.IO.File.OpenRead(finalPath), fileData.fileType));
        }
        [ResponseCache(Duration = 13824000)] //six months
        public async Task <IActionResult> GetFileAsync([FromRoute] long id, [FromQuery] GetFileModify modify)
        {
            if (modify.size > 0)
            {
                if (modify.size < 10)
                {
                    return(BadRequest("Too small!"));
                }
                else if (modify.size <= 100)
                {
                    modify.size = 10 * (modify.size / 10);
                }
                else if (modify.size <= 1000)
                {
                    modify.size = 100 * (modify.size / 100);
                }
                else
                {
                    return(BadRequest("Too large!"));
                }
            }

            var requester = GetRequesterNoFail();

            //Go get that ONE file. This should return null if we can't read it... let's hope!
            //SPECIAL: the 0 file is
            FileView fileData = null;

            if (id == 0)
            {
                if (System.IO.File.Exists(GetAndMakePath(0)))
                {
                    fileData = new FileView()
                    {
                        id = 0, fileType = "image/png"
                    }
                }
                ;
            }
            else
            {
                fileData = (await service.SearchAsync(new FileSearch()
                {
                    Ids = new List <long>()
                    {
                        id
                    },
                    SearchAllBuckets = true
                }, requester)).OnlySingle();
                //FindByIdAsync(id, requester);
            }

            if (fileData == null)
            {
                return(NotFound());
            }

            var finalPath = GetAndMakePath(fileData.id, modify);

            //Ok NOW we can go get it. We may need to perform a resize beforehand if we can't find the file.
            //NOTE: locking on the entire write may increase wait times for brand new images (and image uploads) but it saves cpu cycles
            //in those cases by only resizing an image once.
            await filelock.WaitAsync();

            try
            {
                //Will checking the fileinfo be too much??
                if (!System.IO.File.Exists(finalPath) || (new FileInfo(finalPath)).Length == 0)
                {
                    var          baseImage = GetPath(fileData.id);
                    IImageFormat format;

                    await Task.Run(() =>
                    {
                        using (var image = Image.Load(baseImage, out format))
                        {
                            var maxDim = Math.Max(image.Width, image.Height);
                            var minDim = Math.Min(image.Width, image.Height);
                            var isGif  = format.DefaultMimeType == "image/gif";

                            //Square ALWAYS happens, it can happen before other things.
                            if (modify.crop)
                            {
                                image.Mutate(x => x.Crop(new Rectangle((image.Width - minDim) / 2, (image.Height - minDim) / 2, minDim, minDim)));
                            }

                            //Saving as png also works, but this preserves the format (even if it's a little heavier compute, it's only a one time thing)
                            if (modify.freeze && isGif)
                            {
                                while (image.Frames.Count > 1)
                                {
                                    image.Frames.RemoveFrame(1);
                                }
                            }

                            if (modify.size > 0 && !(isGif && (modify.size > image.Width || modify.size > image.Height)))
                            {
                                var width  = 0;
                                var height = 0;

                                //Preserve aspect ratio when not square
                                if (image.Width > image.Height)
                                {
                                    width = modify.size;
                                }
                                else
                                {
                                    height = modify.size;
                                }

                                image.Mutate(x => x.Resize(width, height));
                            }

                            using (var stream = System.IO.File.OpenWrite(finalPath))
                            {
                                image.Save(stream, format);
                            }
                        }
                    });
                }
            }
            finally
            {
                filelock.Release();
            }

            Response.Headers.Add("ETag", GetETag(finalPath));
            return(File(System.IO.File.OpenRead(finalPath), fileData.fileType));
        }
    /// <summary>
    /// Given an image as a stream, perform the given modifications to it and save it to the given path
    /// </summary>
    /// <param name="fileData"></param>
    /// <param name="savePath"></param>
    /// <param name="modify"></param>
    /// <returns></returns>
    public async Task <ImageManipulationInfo> MakeThumbnailAndSave(Stream fileData, string savePath, GetFileModify modify) //, bool highQualityResize)
    {
        var result = new ImageManipulationInfo {
            RenderCount = 0,
            LoadCount   = 1
        };

        await SingleManipLock.WaitAsync();

        try
        {
            await Task.Run(() =>
            {
                IImageFormat?format;

                using var image = Image.Load(fileData, out format);

                result.MimeType = format.DefaultMimeType;

                //var maxDim = Math.Max(image.Width, image.Height);
                var isGif = format.DefaultMimeType == GifMime;
                var isJpg = format.DefaultMimeType == JpegMime;

                //Square ALWAYS happens, it can happen before other things.
                if (modify.crop)
                {
                    var minDim = Math.Min(image.Width, image.Height);
                    image.Mutate(x => x.Crop(new Rectangle((image.Width - minDim) / 2, (image.Height - minDim) / 2, minDim, minDim)));
                    result.RenderCount++;
                }

                //This must come after the crop!
                var isNowLarger = (modify.size > Math.Max(image.Width, image.Height));

                //Saving as png also works, but this preserves the format (even if it's a little heavier compute, it's only a one time thing)
                if (modify.freeze && isGif)
                {
                    while (image.Frames.Count > 1)
                    {
                        image.Frames.RemoveFrame(1);
                    }
                    result.RenderCount++;
                }

                if (modify.size > 0 && !(isGif && isNowLarger)) //&& (modify.size > image.Width || modify.size > image.Height)))
                {
                    var width  = 0;
                    var height = 0;

                    //Preserve aspect ratio when not square
                    if (image.Width > image.Height)
                    {
                        width = modify.size;
                    }
                    else
                    {
                        height = modify.size;
                    }

                    if (HighQualityResize)
                    {
                        image.Mutate(x => x.Resize(width, height, isNowLarger ? KnownResamplers.Spline : KnownResamplers.Lanczos3));
                    }
                    else
                    {
                        image.Mutate(x => x.Resize(width, height));
                    }

                    result.RenderCount++;
                }

                result.Width  = image.Width;
                result.Height = image.Height;

                using (var stream = System.IO.File.OpenWrite(savePath))
                {
                    IImageEncoder?encoder = null;

                    if (HighQualityResize && modify.size <= MinJpegHighQualitySize && isJpg)
                    {
                        encoder = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder()
                        {
                            Quality = JpegHighQuality,
                        };
                    }

                    if (encoder != null)
                    {
                        image.Save(stream, encoder);
                    }
                    else
                    {
                        image.Save(stream, format);
                    }

                    result.SizeInBytes = stream.Length;
                }
            });
        }
        finally
        {
            SingleManipLock.Release();
        }


        return(result);
    }
Example #9
0
 public Task <ImageManipulationInfo> MakeThumbnailAndSave(Stream fileData, string savePath, GetFileModify modify)
 {
     return(SelfRunSystem.RunProcessWithFileAsync <ImageManipulationInfo>(fileData, SelfRunSystem.RunImageThumbnail, new ImageManipulationMakeThumbnailArgument
     {
         modify = modify,
         savePath = Path.GetFullPath(savePath)
     }));
 }