Пример #1
0
        /// <summary>
        /// Uploads image.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="userId"></param>
        /// <param name="fileName"></param>
        /// <param name="contentType">e.g. "image/jpeg"</param>
        /// <param name="uploadFrom"></param>
        /// <returns></returns>
        public async Task <Media> UploadAsync(Stream source, int userId, string fileName, string contentType,
                                              EUploadedFrom uploadFrom)
        {
            // check if file type is supported
            var ext   = Path.GetExtension(fileName).ToLower();
            var ctype = "." + contentType.Substring(contentType.LastIndexOf("/") + 1).ToLower();

            if (ext.IsNullOrEmpty() || !Accepted_Image_Types.Contains(ext) || !Accepted_Image_Types.Contains(ctype))
            {
                throw new NotSupportedException(ERR_MSG_FILETYPE);
            }

            // check file size
            if (source.Length > MAX_FILE_SIZE)
            {
                throw new FanException(ERR_MSG_FILESIZE);
            }

            // uploadedOn
            var uploadedOn = DateTimeOffset.UtcNow;

            // get the slugged filename and title from original filename
            var(fileNameSlugged, title) = ProcessFileName(fileName, uploadFrom);

            // get unique filename
            var uniqueFileName = await GetUniqueFileNameAsync(fileNameSlugged, uploadedOn);

            // get image resizes
            var resizes = contentType.Equals("image/gif") ?
                          GetImageResizeListForGif(uploadedOn) : GetImageResizeList(uploadedOn);

            return(await _mediaSvc.UploadImageAsync(source, resizes, uniqueFileName, contentType, title,
                                                    uploadedOn, EAppType.Blog, userId, uploadFrom));
        }
Пример #2
0
        /// <summary>
        /// Uploads image by resizing and storing it.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="resizes"></param>
        /// <param name="fileName"></param>
        /// <param name="contentType"></param>
        /// <param name="uploadedOn"></param>
        /// <param name="appType"></param>
        /// <param name="userId"></param>
        /// <param name="uploadFrom"></param>
        /// <returns></returns>
        public async Task <Media> UploadImageAsync(Stream source,
                                                   List <ImageResizeInfo> resizes,
                                                   string fileName,
                                                   string contentType,
                                                   string title,
                                                   DateTimeOffset uploadedOn,
                                                   EAppType appType,
                                                   int userId,
                                                   EUploadedFrom uploadFrom = EUploadedFrom.Browser)
        {
            int resizeCount = 0;

            var(widthOrig, heightOrig) = GetOriginalSize(source);

            foreach (var resize in resizes)
            {
                using (var dest = new MemoryStream())
                {
                    // each time source is read, it needs reset
                    source.Position = 0;

                    // don't resize original png and gif may output large file size, save it as is
                    if (resize.TargetSize == int.MaxValue)
                    {
                        await _storageProvider.SaveFileAsync(source, fileName, resize.Path, resize.PathSeparator);
                    }
                    else if (Math.Max(widthOrig, heightOrig) > resize.TargetSize) // only resize and save when it's larger than target
                    {
                        resizeCount++;
                        Resize(source, dest, resize.TargetSize);
                        dest.Position = 0;
                        await _storageProvider.SaveFileAsync(dest, fileName, resize.Path, resize.PathSeparator);
                    }
                }
            }

            // create record in db
            var media = new Media
            {
                UserId       = userId,
                AppType      = appType,
                FileName     = fileName,
                Title        = title,
                Description  = null,
                Length       = source.Length,
                MediaType    = EMediaType.Image,
                UploadedOn   = uploadedOn,
                UploadedFrom = uploadFrom,
                Width        = widthOrig,
                Height       = heightOrig,
                Caption      = title,
                ContentType  = contentType,
                Alt          = title,
                ResizeCount  = resizeCount,
            };

            await _mediaRepo.CreateAsync(media);

            return(media);
        }
Пример #3
0
        /// <summary>
        /// Uploads image by resizing and storing it.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="resizes"></param>
        /// <param name="fileName"></param>
        /// <param name="contentType"></param>
        /// <param name="uploadedOn"></param>
        /// <param name="appType"></param>
        /// <param name="userId"></param>
        /// <param name="uploadFrom"></param>
        /// <returns></returns>
        public async Task <Media> UploadImageAsync(Stream source,
                                                   List <ImageResizeInfo> resizes,
                                                   string fileName,
                                                   string contentType,
                                                   string title,
                                                   DateTimeOffset uploadedOn,
                                                   EAppType appType,
                                                   int userId,
                                                   EUploadedFrom uploadFrom = EUploadedFrom.Browser)
        {
            int widthOrig;
            int heightOrig;
            int resizeCount = 0;

            if (contentType.Equals("image/gif"))
            {
                using (var imageColl = new MagickImageCollection(source))
                {
                    widthOrig  = imageColl[0].Width;
                    heightOrig = imageColl[0].Height;

                    // currently for gif I only save original so there is no resizing
                    // TODO: with ImageMagick resizing a gif take a long time
                    // plus the resized gif has a larger file size than original
                    // I tried limit gif length to 800px, but even resizing to small has these issues

                    // resize and store
                    foreach (var resize in resizes)
                    {
                        // save original without resizing
                        if (resize.TargetSize == int.MaxValue)
                        {
                            source.Position = 0;
                            await _storageProvider.SaveFileAsync(source, fileName, resize.Path, resize.PathSeparator);
                        }
                        //else if (Math.Max(widthOrig, heightOrig) > resize.TargetSize)
                        //{
                        //    resizeCount++;
                        //    var (width, height) = GetNewSize(widthOrig, heightOrig, resize.TargetSize);

                        //    imageColl.Coalesce();
                        //    foreach (var image in imageColl)
                        //    {
                        //        var colors = image.TotalColors;
                        //        image.Resize(width, height); // resize will make # of colors higher
                        //        image.Quantize(new QuantizeSettings
                        //        {
                        //            Colors = colors, // set it back to the smaller original colors
                        //            DitherMethod = DitherMethod.No
                        //        });
                        //    }

                        //    imageColl.Optimize();
                        //    await _storageProvider.SaveFileAsync(imageColl.ToByteArray(), fileName, resize.Path, resize.PathSeparator);
                        //}
                    }
                }
            }
            else if (contentType.Equals("image/png"))
            {
                using (var image = new MagickImage(source))
                {
                    widthOrig  = image.Width;
                    heightOrig = image.Height;

                    foreach (var resize in resizes)
                    {
                        // save original without resizing for png
                        if (resize.TargetSize == int.MaxValue)
                        {
                            source.Position = 0;
                            await _storageProvider.SaveFileAsync(source, fileName, resize.Path, resize.PathSeparator);
                        }
                        else if (Math.Max(widthOrig, heightOrig) > resize.TargetSize)
                        {
                            resizeCount++;
                            var(width, height) = GetNewSize(widthOrig, heightOrig, resize.TargetSize);
                            //image.Quality = 75; // does not seem to affect output file size, so commented out
                            image.Resize(width, height);

                            await _storageProvider.SaveFileAsync(image.ToByteArray(), fileName, resize.Path, resize.PathSeparator);
                        }
                    }
                }
            }
            else // jpg
            {
                using (var image = new MagickImage(source))
                {
                    widthOrig  = image.Width;
                    heightOrig = image.Height;

                    foreach (var resize in resizes)
                    {
                        if (resize.TargetSize == int.MaxValue || Math.Max(widthOrig, heightOrig) > resize.TargetSize)
                        {
                            if (resize.TargetSize != int.MaxValue)
                            {
                                resizeCount++;
                            }
                            var(width, height) = GetNewSize(widthOrig, heightOrig, resize.TargetSize);
                            // setting the Quality is needed for having it here makes a difference for a smaller output file size!
                            image.Quality = 85; // 75 is default
                            image.Resize(width, height);

                            await _storageProvider.SaveFileAsync(image.ToByteArray(), fileName, resize.Path, resize.PathSeparator);
                        }
                    }
                }
            }

            // create record in db
            var media = new Media
            {
                UserId       = userId,
                AppType      = appType,
                FileName     = fileName,
                Title        = title,
                Description  = null,
                Length       = source.Length,
                MediaType    = EMediaType.Image,
                UploadedOn   = uploadedOn,
                UploadedFrom = uploadFrom,
                Width        = widthOrig,
                Height       = heightOrig,
                Caption      = title,
                ContentType  = contentType,
                Alt          = title,
                ResizeCount  = resizeCount,
            };

            await _mediaRepo.CreateAsync(media);

            return(media);
        }
Пример #4
0
        /// <summary>
        /// Takes the original filename and returns a slugged filename and title attribute.
        /// </summary>
        /// <remarks>
        /// If the filename is too long it shorten it. Then it generates a slugged filename which
        /// is hyphen separeated value for english original filenames, a random string value for
        /// non-english filenames.  The title attribute is original filename html-encoded for safe
        /// display.
        /// </remarks>
        /// <param name="fileNameOrig">Original filename user is uploading.</param>
        /// <param name="uploadFrom">This is used solely because of olw quirks I have to handle.</param>
        /// <returns></returns>
        private (string fileNameSlugged, string title) ProcessFileName(string fileNameOrig, EUploadedFrom uploadFrom)
        {
            // extra filename without ext, note this will also remove the extra path info from OLW
            var fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileNameOrig);

            // make sure file name is not too long
            if (fileNameWithoutExt.Length > MediaService.MEDIA_FILENAME_MAXLEN)
            {
                fileNameWithoutExt = fileNameWithoutExt.Substring(0, MediaService.MEDIA_FILENAME_MAXLEN);
            }

            // there is a quirk file uploaded from olw had "_2" suffixed to the name
            if (uploadFrom == EUploadedFrom.MetaWeblog && fileNameWithoutExt.EndsWith("_2"))
            {
                fileNameWithoutExt = fileNameWithoutExt.Remove(fileNameWithoutExt.Length - 2);
            }

            // slug file name
            var slug = Util.Slugify(fileNameWithoutExt);

            if (slug.IsNullOrEmpty()) // slug may end up empty
            {
                slug = Util.RandomString(6);
            }
            else if (uploadFrom == EUploadedFrom.MetaWeblog && slug == "thumb") // or may end up with only "thumb" for olw
            {
                slug = string.Concat(Util.RandomString(6), "_thumb");
            }

            var ext             = Path.GetExtension(fileNameOrig).ToLower();
            var fileNameSlugged = $"{slug}{ext}";
            var fileNameEncoded = WebUtility.HtmlEncode(fileNameWithoutExt);

            return(fileNameSlugged : fileNameSlugged, title : fileNameEncoded);
        }
Пример #5
0
        /// <summary>
        /// Returns media url after upload to storage.
        /// </summary>
        /// <param name="userId">Id of the user uploading the media.</param>
        /// <param name="fileName">File name with ext.</param>
        /// <param name="content">File content</param>
        /// <param name="appId">Which app it uploaded it.</param>
        /// <returns></returns>
        /// <remarks>
        /// Note: This method is optimized for metaweblog use with olw, other apps have totally
        /// different file logic.
        ///
        /// Depending on the storage provider, the returned media url could be relative path
        /// (File Sys) or absolute path (Azure Blog).
        /// </remarks>
        public async Task <string> UploadMediaAsync(int userId, string fileName, byte[] content, EAppType appId, EUploadedFrom uploadFrom)
        {
            // verify ext is supported
            var ext = Path.GetExtension(fileName);

            if (ext.IsNullOrEmpty() || !Accepted_Image_Types.Contains(ext, StringComparer.InvariantCultureIgnoreCase))
            {
                throw new FanException("Upload file type is not supported.");
            }

            // time
            var uploadedOn = DateTimeOffset.UtcNow;
            var year       = uploadedOn.Year.ToString();
            var month      = uploadedOn.Month.ToString("d2");

            // make sure file name is not too long
            var fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);

            if (fileNameWithoutExt.Length > MEDIA_FILENAME_MAXLEN)
            {
                fileNameWithoutExt = fileNameWithoutExt.Substring(0, MEDIA_FILENAME_MAXLEN);
            }

            // there is a quirk file uploaded from olw had "_2" suffixed to the name
            if (uploadFrom == EUploadedFrom.MetaWeblog && fileNameWithoutExt.EndsWith("_2"))
            {
                fileNameWithoutExt = fileNameWithoutExt.Remove(fileNameWithoutExt.Length - 2);
            }

            // slug file name
            // chinese fn ends up emtpy and the thumb file with chinese fn ends up with only "thumb"
            var slug = Util.FormatSlug(fileNameWithoutExt);

            if (slug.IsNullOrEmpty())
            {
                slug = Util.RandomString(6);
            }
            else if (uploadFrom == EUploadedFrom.MetaWeblog && slug == "thumb")
            {
                slug = string.Concat(Util.RandomString(6), "_thumb");
            }
            string fileNameSlugged = $"{slug}{ext}";

            // save file to storage and get back file path
            var filePath = await _storageProvider.SaveFileAsync(userId, fileNameSlugged, year, month, content, EAppType.Blog);

            // encode filename
            var fileNameEncoded = WebUtility.HtmlEncode(fileNameWithoutExt);

            // since file name could have been updated for uniqueness
            var start          = filePath.LastIndexOf('/') + 1;
            var uniqueFileName = filePath.Substring(start, filePath.Length - start);

            // save record to db
            var media = new Media
            {
                UserId       = userId,
                AppId        = appId,
                FileName     = uniqueFileName,  // unique filename from storage provider
                Title        = fileNameEncoded, // original filename
                Description  = null,
                Length       = content.LongLength,
                MediaType    = EMediaType.Image,
                UploadedOn   = uploadedOn,
                UploadedFrom = uploadFrom,
            };
            await _mediaRepo.CreateAsync(media);

            return(filePath);
        }
Пример #6
0
        /// <summary>
        /// Uploads image by resizing and storing it.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="resizes"></param>
        /// <param name="fileName"></param>
        /// <param name="contentType"></param>
        /// <param name="uploadedOn"></param>
        /// <param name="appType"></param>
        /// <param name="userId"></param>
        /// <param name="uploadFrom"></param>
        /// <returns></returns>
        public async Task <Media> UploadImageAsync(Stream source,
                                                   List <ImageResizeInfo> resizes,
                                                   string fileName,
                                                   string contentType,
                                                   string title,
                                                   DateTimeOffset uploadedOn,
                                                   EAppType appType,
                                                   int userId,
                                                   EUploadedFrom uploadFrom = EUploadedFrom.Browser)
        {
            int widthOrig;
            int heightOrig;
            int resizeCount = 0;

            if (contentType.Equals("image/gif"))
            {
                using (var imageColl = new MagickImageCollection(source))
                {
                    widthOrig  = imageColl[0].Width;
                    heightOrig = imageColl[0].Height;

                    // resize and store
                    foreach (var resize in resizes)
                    {
                        // save original without resizing, currently I couldn't dec original file size by resizing with ImageMagick
                        if (resize.TargetSize == int.MaxValue)
                        {
                            source.Position = 0;
                            await _storageProvider.SaveFileAsync(source, fileName, resize.Path, resize.PathSeparator);
                        }
                        else if (Math.Max(widthOrig, heightOrig) > resize.TargetSize)
                        {
                            resizeCount++;
                            var(width, height) = GetNewSize(widthOrig, heightOrig, resize.TargetSize);

                            imageColl.Coalesce();
                            foreach (var image in imageColl)
                            {
                                var colors = image.TotalColors;
                                image.Resize(width, height); // resize will make # of colors higher
                                image.Quantize(new QuantizeSettings
                                {
                                    Colors       = colors, // set it back to the smaller original colors
                                    DitherMethod = DitherMethod.No
                                });
                            }

                            imageColl.Optimize();
                            await _storageProvider.SaveFileAsync(imageColl.ToByteArray(), fileName, resize.Path, resize.PathSeparator);
                        }
                    }
                }
            }
            else if (contentType.Equals("image/png"))
            {
                using (var image = new MagickImage(source))
                {
                    widthOrig  = image.Width;
                    heightOrig = image.Height;

                    foreach (var resize in resizes)
                    {
                        // save original without resizing for png
                        if (resize.TargetSize == int.MaxValue)
                        {
                            source.Position = 0;
                            await _storageProvider.SaveFileAsync(source, fileName, resize.Path, resize.PathSeparator);
                        }
                        else if (Math.Max(widthOrig, heightOrig) > resize.TargetSize)
                        {
                            resizeCount++;
                            var(width, height) = GetNewSize(widthOrig, heightOrig, resize.TargetSize);
                            //image.Quality = 75; // does not seem to affect output file size, so commented out
                            image.Resize(width, height);

                            await _storageProvider.SaveFileAsync(image.ToByteArray(), fileName, resize.Path, resize.PathSeparator);
                        }
                    }
                }
            }
            else // jpg
            {
                using (var image = new MagickImage(source))
                {
                    widthOrig  = image.Width;
                    heightOrig = image.Height;

                    foreach (var resize in resizes)
                    {
                        if (resize.TargetSize == int.MaxValue || Math.Max(widthOrig, heightOrig) > resize.TargetSize)
                        {
                            if (resize.TargetSize != int.MaxValue)
                            {
                                resizeCount++;
                            }
                            var(width, height) = GetNewSize(widthOrig, heightOrig, resize.TargetSize);
                            image.Quality      = 75; // though 75 is default, having it here does make a difference on making output file size smaller!
                            image.Resize(width, height);

                            await _storageProvider.SaveFileAsync(image.ToByteArray(), fileName, resize.Path, resize.PathSeparator);
                        }
                    }
                }
            }

            // create record in db
            var media = new Media
            {
                UserId       = userId,
                AppType      = appType,
                FileName     = fileName,
                Title        = title,
                Description  = null,
                Length       = source.Length,
                MediaType    = EMediaType.Image,
                UploadedOn   = uploadedOn,
                UploadedFrom = uploadFrom,
                Width        = widthOrig,
                Height       = heightOrig,
                Caption      = title,
                ContentType  = contentType,
                Alt          = title,
                ResizeCount  = resizeCount,
            };

            await _mediaRepo.CreateAsync(media);

            return(media);
        }