Beispiel #1
0
        public async Task <FileOperationResult> CopyFileAsync(MediaFileInfo mediaFile, string destinationFileName, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError)
        {
            Guard.NotNull(mediaFile, nameof(mediaFile));
            Guard.NotEmpty(destinationFileName, nameof(destinationFileName));

            var destPathData = CreateDestinationPathData(mediaFile.File, destinationFileName);
            var destFileName = destPathData.FileName;
            var destFolderId = destPathData.Folder.Id;

            var dupe = mediaFile.FolderId == destPathData.Folder.Id
                       // Source folder equals dest folder, so same file
                ? mediaFile.File
                       // Another dest folder, check for duplicate by file name
                : await _db.MediaFiles.FirstOrDefaultAsync(x => x.Name == destFileName && x.FolderId == destFolderId);

            var copyResult = await InternalCopyFile(
                mediaFile.File,
                destPathData,
                true /* copyData */,
                (DuplicateEntryHandling)((int)dupeFileHandling),
                () => Task.FromResult(dupe),
                p => CheckUniqueFileName(p));

            return(new FileOperationResult
            {
                Operation = "copy",
                DuplicateFileHandling = dupeFileHandling,
                SourceFile = mediaFile,
                DestinationFile = ConvertMediaFile(copyResult.Copy, destPathData.Folder),
                IsDuplicate = copyResult.IsDupe,
                UniquePath = copyResult.IsDupe ? destPathData.FullPath : null
            });
        }
Beispiel #2
0
        protected MediaFileInfo ConvertMediaFile(MediaFile file, MediaFolderNode folder)
        {
            var mediaFile = new MediaFileInfo(file, _storageProvider, _urlGenerator, folder?.Path)
            {
                ThumbSize = _mediaSettings.ProductThumbPictureSize
            };

            return(mediaFile);
        }
        public static string GetUrl(this IMediaService service, MediaFileInfo file, int thumbnailSize, string host = null, bool doFallback = true)
        {
            ProcessImageQuery query = thumbnailSize > 0
                ? new ProcessImageQuery {
                MaxSize = thumbnailSize
            }
                : null;

            return(service.GetUrl(file, query, host, doFallback));
        }
Beispiel #4
0
        public async Task Invoke(HttpContext context, IMediaService mediaService, IMediaUrlGenerator mediaUrlGenerator)
        {
            var path = context.GetRouteValueAs <string>("path");

            if (path.IsEmpty())
            {
                // Cannot operate without path
                context.Response.ContentType = "text/html";
                context.Response.StatusCode  = 404;
                await context.Response.WriteAsync("404: Not Found");

                return;
            }

            var mediaFileId = context.GetRouteValueAs <int?>("id");

            MediaFileInfo mediaFile = null;

            if (mediaFileId.HasValue)
            {
                /// Redirect legacy URL "/{pub}/image/234/file.png" to "/{pub}/234/catalog/path/to/file.png"
                mediaFile = await mediaService.GetFileByIdAsync(mediaFileId.Value, MediaLoadFlags.AsNoTracking);
            }
            else
            {
                // Redirect legacy URL "/{tenant?}/uploaded/some/file.png" to "/file/1234/some/file.png"
                mediaFile = await mediaService.GetFileByPathAsync(
                    SystemAlbumProvider.Files + "/" + path,
                    MediaLoadFlags.AsNoTracking);
            }

            var url = mediaUrlGenerator.GenerateUrl(mediaFile, context.Request.QueryString, string.Empty, false);

            if (url.IsEmpty())
            {
                // Cannot redirect, return 404
                context.Response.ContentType = mediaFile?.MimeType ?? "text/html";
                context.Response.StatusCode  = 404;
                await context.Response.WriteAsync("404: Not Found");
            }
            else
            {
                // Redirect to new location
                context.Response.StatusCode = context.Connection.IsLocal() ? 302 : 301;
                context.Response.Headers[HeaderNames.Location] = path;
            }
        }
Beispiel #5
0
        public virtual string GenerateUrl(
            MediaFileInfo file,
            QueryString query = default,
            string host       = null,
            bool doFallback   = true)
        {
            string path;

            // Build virtual path with pattern "media/{id}/{album}/{dir}/{NameWithExt}"
            if (file?.Path != null)
            {
                path = _processedImagesRootPath + file.Id.ToString(CultureInfo.InvariantCulture) + "/" + file.Path;
            }
            else if (doFallback)
            {
                path = _processedImagesRootPath + "0/" + _fallbackImageFileName;
            }
            else
            {
                return(null);
            }

            if (host == null)
            {
                host = _host;
            }
            else if (host == string.Empty)
            {
                host = _pathBase;
            }
            else
            {
                host = host.EnsureEndsWith('/');
            }

            var url = host;

            // Strip leading "/", the host/pathBase has this already
            if (path[0] == '/')
            {
                path = path[1..];
Beispiel #6
0
 public string GetUrl(MediaFileInfo file, ProcessImageQuery imageQuery, string host = null, bool doFallback = true)
 {
     return(_urlGenerator.GenerateUrl(file, imageQuery, host, doFallback));
 }
Beispiel #7
0
        public async Task Invoke(
            HttpContext context,
            IMediaService mediaService,
            IFolderService folderService,
            IPermissionService permissionService,
            IWorkContext workContext,
            MediaSettings mediaSettings,
            MediaHelper mediaHelper,
            Lazy <IEnumerable <IMediaHandler> > mediaHandlers,
            ILogger <MediaMiddleware> logger)
        {
            var mediaFileId = context.GetRouteValueAs <int>("id");
            var path        = context.GetRouteValueAs <string>("path");

            if (context.Request.Method != HttpMethods.Get && context.Request.Method != HttpMethods.Head)
            {
                await NotFound(null);

                return;
            }

            var method = context.Request.Method;

            MediaFileInfo mediaFile = null;
            MediaPathData pathData  = null;

            if (mediaFileId == 0)
            {
                // This is most likely a request for a default placeholder image
                pathData = new MediaPathData(path);
            }
            else if (!mediaHelper.TokenizePath(path, false, out pathData))
            {
                // Missing or malformed Uri: get file metadata from DB by id, but only when current user has media manage rights
                if (!(await permissionService.AuthorizeAsync(Permissions.Media.Update)))
                {
                    await NotFound(null);

                    return;
                }

                mediaFile = await mediaService.GetFileByIdAsync(mediaFileId, MediaLoadFlags.AsNoTracking);

                if (mediaFile == null || mediaFile.FolderId == null || mediaFile.Deleted)
                {
                    await NotFound(mediaFile?.MimeType);

                    return;
                }

                pathData = new MediaPathData(folderService.GetNodeById(mediaFile.FolderId.Value), mediaFile.Name)
                {
                    Extension = mediaFile.Extension,
                    MimeType  = mediaFile.MimeType
                };
            }

            var q = await CreateImageQuery(context, pathData.MimeType, pathData.Extension);

            // Security: check allowed thumnail sizes and return 404 if disallowed.
            var thumbMaxWidth    = q.MaxWidth;
            var thumbMaxHeight   = q.MaxHeight;
            var thumbSizeAllowed = IsThumbnailSizeAllowed(thumbMaxWidth) && (thumbMaxHeight == thumbMaxWidth || IsThumbnailSizeAllowed(thumbMaxHeight));

            if (!thumbSizeAllowed)
            {
                await NotFound(pathData.MimeType);

                return;
            }

            // Create the handler context
            var handlerContext = new MediaHandlerContext
            {
                HttpContext       = context,
                CurrentCustomer   = workContext.CurrentCustomer,
                PermissionService = permissionService,
                MediaFileId       = mediaFileId,
                RawPath           = path,
                MediaService      = mediaService,
                PathData          = pathData,
                ImageQuery        = q
            };

            handlerContext.SetSourceFile(mediaFile);

            var handlers = mediaHandlers.Value.OrderBy(x => x.Order).ToArray();

            // Run every registered media handler to obtain a thumbnail for the requested media file
            IMediaHandler currentHandler;

            for (var i = 0; i < handlers.Length; i++)
            {
                currentHandler = handlers[i];

                // Execute handler
                await currentHandler.ExecuteAsync(handlerContext);

                if (handlerContext.Exception != null)
                {
                    var isThumbExtractFail = handlerContext.Exception is ExtractThumbnailException;
                    var statusCode         = isThumbExtractFail ? StatusCodes.Status204NoContent : StatusCodes.Status500InternalServerError;
                    var statusMessage      = isThumbExtractFail ? handlerContext.Exception.InnerException?.Message.EmptyNull() : handlerContext.Exception.Message;

                    await SendStatus(statusCode, statusMessage);

                    return;
                }

                if (handlerContext.Executed || handlerContext.ResultFile != null)
                {
                    // Get out if the handler produced a result file or has been executed in any way
                    break;
                }
            }

            try
            {
                var responseFile = handlerContext.ResultFile ?? await handlerContext.GetSourceFileAsync();

                if (responseFile == null || !responseFile.Exists)
                {
                    await NotFound(pathData.MimeType);

                    return;
                }

                if (string.Equals(responseFile.Extension, "." + pathData.Extension, StringComparison.CurrentCultureIgnoreCase))
                {
                    pathData.MimeType = MimeTypes.MapNameToMimeType(responseFile.Extension);
                }

                // Create FileStreamResult object
                var fileResult = CreateFileResult(responseFile, pathData);

                // Cache control
                ApplyResponseCaching(context, mediaSettings);

                // INFO: Although we are outside of the MVC pipeline we gonna use ActionContext anyway, because "FileStreamResult"
                // does everything we need (ByteRange, ETag etc.), so wo we gonna use it instead of reinventing the wheel.
                // A look at the MVC source code reveals that HttpContext is the only property that gets accessed, therefore we can omit
                // all the other stuff like ActionDescriptor or ModelState (which we cannot access or create from a middleware anyway).
                await fileResult.ExecuteResultAsync(new ActionContext { HttpContext = context, RouteData = context.GetRouteData() });
            }
            finally
            {
                var imageProcessor = context.RequestServices.GetRequiredService <IImageProcessor>();
                logger.Debug("ImageProcessor TOTAL: {0} ms.", imageProcessor.TotalProcessingTimeMs);
            }

            #region Functions

            bool IsThumbnailSizeAllowed(int?size)
            {
                return(size.GetValueOrDefault() == 0 ||
                       mediaSettings.IsAllowedThumbnailSize(size.Value) ||
                       permissionService.Authorize(Permissions.Media.Update, workContext.CurrentCustomer));
            }

            async Task NotFound(string mime)
            {
                context.Response.ContentType = mime.NullEmpty() ?? "text/html";
                context.Response.StatusCode  = 404;
                await context.Response.WriteAsync("404: Not Found");
            }

            async Task SendStatus(int code, string message)
            {
                context.Response.StatusCode = code;
                await context.Response.WriteAsync(message);
            }

            #endregion
        }
        public static string Media(this IUrlHelper urlHelper, MediaFileInfo file, ProcessImageQuery query, string host = null, bool doFallback = true)
        {
            var mediaService = urlHelper.ActionContext.HttpContext.RequestServices.GetRequiredService <IMediaService>();

            return(mediaService.GetUrl(file, query, host, doFallback));
        }
        public static string Media(this IUrlHelper urlHelper, MediaFileInfo file, int thumbnailSize = 0, string host = null, bool doFallback = true)
        {
            var mediaService = urlHelper.ActionContext.HttpContext.RequestServices.GetRequiredService <IMediaService>();

            return(mediaService.GetUrl(file, thumbnailSize, host, doFallback));
        }
Beispiel #10
0
 public DuplicateMediaFileException(string message, MediaFileInfo dupeFile, string uniquePath) : base(message)
 {
     File       = dupeFile;
     UniquePath = uniquePath;
 }
Beispiel #11
0
 public InvalidOperationException IdenticalPaths(MediaFileInfo file)
 {
     Guard.NotNull(file, nameof(file));
     return(new InvalidOperationException(T("Admin.Media.Exception.FileNamesIdentical", file.Path)));
 }
Beispiel #12
0
 public DuplicateMediaFileException DuplicateFile(string fullPath, MediaFileInfo dupeFile, string uniquePath)
 {
     return(new DuplicateMediaFileException(T("Admin.Media.Exception.DuplicateFile", fullPath), dupeFile, uniquePath));
 }