示例#1
0
        /// <summary>
        /// Post processes the image.
        /// </summary>
        /// <param name="context">The current context.</param>
        /// <param name="stream">The source image stream.</param>
        /// <param name="extension">The image extension.</param>
        /// <returns>
        /// The <see cref="MemoryStream" />.
        /// </returns>
        public static async Task <MemoryStream> PostProcessImageAsync(HttpContext context, MemoryStream stream, string extension)
        {
            var postProcessorBootstrapper = PostProcessorBootstrapper.Instance;

            if (postProcessorBootstrapper.IsInstalled && stream.Length is var length && length > 0)
            {
                string sourceFile = null, destinationFile = null;
                try
                {
                    // Get temporary file names
                    var tempPath = Path.GetTempPath();
                    var tempFile = Path.GetFileNameWithoutExtension(Path.GetRandomFileName());
                    sourceFile      = Path.Combine(tempPath, Path.ChangeExtension(tempFile, extension));
                    destinationFile = Path.Combine(tempPath, Path.ChangeExtension(tempFile + "-out", extension));

                    // Get processes to start
                    var processStartInfos = GetProcessStartInfos(extension, length, sourceFile, destinationFile).ToList();
                    if (processStartInfos.Count > 0)
                    {
                        // Save the input stream to our source temp file for post processing
                        var sourceFileInfo = new FileInfo(sourceFile);
                        using (var fileStream = sourceFileInfo.Create())
                        {
                            // Try to keep the file in memory and ensure it's not indexed
                            sourceFileInfo.Attributes |= FileAttributes.Temporary | FileAttributes.NotContentIndexed;

                            await stream.CopyToAsync(fileStream).ConfigureAwait(false);
                        }

                        // Create cancellation token with timeout
                        using (var cancellationTokenSource = new CancellationTokenSource(postProcessorBootstrapper.Timeout))
                        {
                            var remainingProcesses = processStartInfos.Count;
                            foreach (var processStartInfo in processStartInfos)
                            {
                                // Set default properties
                                processStartInfo.FileName       = Path.Combine(postProcessorBootstrapper.WorkingPath, processStartInfo.FileName);
                                processStartInfo.CreateNoWindow = true;
                                processStartInfo.WindowStyle    = ProcessWindowStyle.Hidden;

                                // Run process
                                using (var processResults = await ProcessEx.RunAsync(processStartInfo, cancellationTokenSource.Token).ConfigureAwait(false))
                                {
                                    if (processResults.ExitCode == 1)
                                    {
                                        ImageProcessorBootstrapper.Instance.Logger.Log(typeof(PostProcessor), $"Unable to post process image for request {context.Request.Unvalidated.Url}, {processStartInfo.FileName} {processStartInfo.Arguments} exited with error code 1. Original image returned.");
                                        break;
                                    }
                                }

                                remainingProcesses--;

                                var destinationFileInfo = new FileInfo(destinationFile);
                                if (destinationFileInfo.Exists)
                                {
                                    // Delete the source file
                                    sourceFileInfo.IsReadOnly = false;
                                    sourceFileInfo.Delete();

                                    if (remainingProcesses > 0)
                                    {
                                        // Use destination file as new source (for the next process)
                                        destinationFileInfo.MoveTo(sourceFile);
                                    }

                                    // Swap source for destination
                                    sourceFileInfo = destinationFileInfo;

                                    // Try to keep the file in memory and ensure it's not indexed
                                    sourceFileInfo.Attributes |= FileAttributes.Temporary | FileAttributes.NotContentIndexed;
                                }
                            }
                        }

                        // Refresh source file (because it's changed by external processes)
                        sourceFileInfo.Refresh();
                        if (sourceFileInfo.Exists && sourceFileInfo.Length < length)
                        {
                            // Save result back to stream
                            using (var fileStream = sourceFileInfo.OpenRead())
                            {
                                stream.SetLength(0);
                                await fileStream.CopyToAsync(stream).ConfigureAwait(false);
                            }
                        }
                    }
                }
                catch (OperationCanceledException)
                {
                    ImageProcessorBootstrapper.Instance.Logger.Log(typeof(PostProcessor), $"Unable to post process image for request {context.Request.Unvalidated.Url} within {postProcessorBootstrapper.Timeout}ms. Original image returned.");
                }
                catch (Exception ex)
                {
                    // Some security policies don't allow execution of programs in this way
                    ImageProcessorBootstrapper.Instance.Logger.Log(typeof(PostProcessor), ex.Message);
                }
                finally
                {
                    // Set position back to begin
                    stream.Position = 0;

                    // Always cleanup files
                    if (sourceFile != null)
                    {
                        try
                        {
                            var sourceFileInfo = new FileInfo(sourceFile);
                            if (sourceFileInfo.Exists)
                            {
                                sourceFileInfo.IsReadOnly = false;
                                sourceFileInfo.Delete();
                            }
                        }
                        catch
                        {
                            // Normally a no no, but logging would be excessive + temp files get cleaned up eventually
                        }
                    }

                    if (destinationFile != null)
                    {
                        try
                        {
                            var destinationFileInfo = new FileInfo(destinationFile);
                            if (destinationFileInfo.Exists)
                            {
                                destinationFileInfo.IsReadOnly = false;
                                destinationFileInfo.Delete();
                            }
                        }
                        catch
                        {
                            // Normally a no no, but logging would be excessive + temp files get cleaned up eventually
                        }
                    }
                }
            }

            // ALways return stream (even if it's not optimized)
            return(stream);
        }
示例#2
0
        /// <summary>
        /// Post processes the image.
        /// </summary>
        /// <param name="context">The current context.</param>
        /// <param name="stream">The source image stream.</param>
        /// <param name="extension">The image extension.</param>
        /// <returns>
        /// The <see cref="MemoryStream" />.
        /// </returns>
        public static async Task <MemoryStream> PostProcessImageAsync(HttpContext context, MemoryStream stream, string extension)
        {
            if (!PostProcessorBootstrapper.Instance.IsInstalled)
            {
                return(stream);
            }

            string sourceFile = null, destinationFile = null;
            var    timeout = PostProcessorBootstrapper.Instance.Timout;

            try
            {
                // Save source file length
                var length = stream.Length;

                // Create a temporary source file with the correct extension
                var tempSourceFile = Path.GetTempFileName();
                sourceFile = Path.ChangeExtension(tempSourceFile, extension);
                File.Move(tempSourceFile, sourceFile);

                // Give our destination file a unique name
                destinationFile = sourceFile.Replace(extension, "-out" + extension);

                // Get processes to start
                var processStartInfos = GetProcessStartInfos(extension, length, sourceFile, destinationFile).ToList();
                if (processStartInfos.Count > 0)
                {
                    // Save the input stream to our source temp file for post processing
                    using (var fileStream = File.Create(sourceFile))
                    {
                        await stream.CopyToAsync(fileStream).ConfigureAwait(false);
                    }

                    // Create cancellation token with timeout
                    using (var cancellationTokenSource = new CancellationTokenSource(timeout))
                    {
                        foreach (var processStartInfo in processStartInfos)
                        {
                            // Use destination file as new source (if previous process created one).
                            if (File.Exists(destinationFile))
                            {
                                File.Copy(destinationFile, sourceFile, true);
                            }

                            // Set default properties
                            processStartInfo.FileName       = Path.Combine(PostProcessorBootstrapper.Instance.WorkingPath, processStartInfo.FileName);
                            processStartInfo.CreateNoWindow = true;
                            processStartInfo.WindowStyle    = ProcessWindowStyle.Hidden;

                            // Run process
                            using (var processResults = await ProcessEx.RunAsync(processStartInfo, cancellationTokenSource.Token).ConfigureAwait(false))
                            {
                                if (processResults.ExitCode == 1)
                                {
                                    ImageProcessorBootstrapper.Instance.Logger.Log(typeof(PostProcessor), $"Unable to post process image for request {context.Request.Unvalidated.Url}, {processStartInfo.FileName} {processStartInfo.Arguments} exited with error code 1. Original image returned.");
                                    break;
                                }
                            }
                        }
                    }

                    // Save result
                    if (!File.Exists(destinationFile))
                    {
                        File.Copy(sourceFile, destinationFile, true);
                    }

                    var result = new PostProcessingResultEventArgs(destinationFile, length);
                    if (result.ResultFileSize > 0 && result.Saving > 0)
                    {
                        using (var fileStream = File.OpenRead(destinationFile))
                        {
                            stream.SetLength(0);
                            await fileStream.CopyToAsync(stream).ConfigureAwait(false);
                        }
                    }
                }
            }
            catch (OperationCanceledException)
            {
                ImageProcessorBootstrapper.Instance.Logger.Log(typeof(PostProcessor), $"Unable to post process image for request {context.Request.Unvalidated.Url} within {timeout}ms. Original image returned.");
            }
            catch (Exception ex)
            {
                // Some security policies don't allow execution of programs in this way
                ImageProcessorBootstrapper.Instance.Logger.Log(typeof(PostProcessor), ex.Message);
            }
            finally
            {
                // Always cleanup files
                try
                {
                    // Ensure files exist, are not read only, and delete
                    if (sourceFile != null && File.Exists(sourceFile))
                    {
                        File.SetAttributes(sourceFile, FileAttributes.Normal);
                        File.Delete(sourceFile);
                    }

                    if (destinationFile != null && File.Exists(destinationFile))
                    {
                        File.SetAttributes(destinationFile, FileAttributes.Normal);
                        File.Delete(destinationFile);
                    }
                }
                catch
                {
                    // Normally a no no, but logging would be excessive + temp files get cleaned up eventually.
                }
            }

            // ALways return stream (even if it's not optimized)
            stream.Position = 0;
            return(stream);
        }