The post processing result event arguments. Many thanks to Azure Image Optimizer
Inheritance: System.EventArgs
Exemplo n.º 1
0
        /// <summary>
        /// Post processes the image.
        /// </summary>
        /// <param name="stream">The source image stream.</param>
        /// <param name="extension">The image extension.</param>
        /// <returns>
        /// The <see cref="MemoryStream"/>.
        /// </returns>
        public static MemoryStream PostProcessImage(MemoryStream stream, string extension)
        {
            // Create a source temporary file with the correct extension.
            long   length     = stream.Length;
            string tempFile   = Path.GetTempFileName();
            string sourceFile = Path.ChangeExtension(tempFile, extension);

            File.Move(tempFile, sourceFile);

            // Save the input stream to a temp file for post processing.
            using (FileStream fileStream = File.Create(sourceFile))
            {
                stream.CopyTo(fileStream);
            }

            PostProcessingResultEventArgs result = RunProcess(sourceFile, length);

            if (result != null && result.Saving > 0)
            {
                using (FileStream fileStream = File.OpenRead(sourceFile))
                {
                    // Replace stream contents.
                    stream.SetLength(0);
                    fileStream.CopyTo(stream);
                }
            }

            // Cleanup
            File.Delete(sourceFile);

            stream.Position = 0;

            return(stream);
        }
Exemplo n.º 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 MemoryStream PostProcessImage(HttpContext context, MemoryStream stream, string extension)
        {
            if (!PostProcessorBootstrapper.Instance.IsInstalled)
            {
                return(stream);
            }

            // Create a temporary source file with the correct extension.
            long length = stream.Length;

            string tempSourceFile = Path.GetTempFileName();
            string sourceFile     = Path.ChangeExtension(tempSourceFile, extension);

            File.Move(tempSourceFile, sourceFile);

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

            // Save the input stream to our source temp file for post processing.
            using (FileStream fileStream = File.Create(sourceFile))
            {
                stream.CopyTo(fileStream);
            }

            PostProcessingResultEventArgs result = RunProcess(context.Request.Unvalidated.Url, sourceFile, destinationFile, length);

            // If our result is good and a saving is made we replace our original stream contents with our new compressed file.
            if (result != null && result.ResultFileSize > 0 && result.Saving > 0)
            {
                using (FileStream fileStream = File.OpenRead(destinationFile))
                {
                    stream.SetLength(0);
                    fileStream.CopyTo(stream);
                }
            }

            // Cleanup the temp files.
            try
            {
                // Ensure files exist, are not read only, and delete
                if (File.Exists(sourceFile))
                {
                    File.SetAttributes(sourceFile, FileAttributes.Normal);
                    File.Delete(sourceFile);
                }

                if (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.
            }

            stream.Position = 0;
            return(stream);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Runs the process to optimize the images.
        /// </summary>
        /// <param name="sourceFile">The source file.</param>
        /// <param name="length">The source file length in bytes.</param>
        /// <returns>
        /// The <see cref="Task{PostProcessingResultEventArgs}"/> containing post-processing information.
        /// </returns>
        private static PostProcessingResultEventArgs RunProcess(string sourceFile, long length)
        {
            PostProcessingResultEventArgs result = null;
            ProcessStartInfo start = new ProcessStartInfo("cmd")
            {
                WindowStyle      = ProcessWindowStyle.Hidden,
                WorkingDirectory = PostProcessorBootstrapper.Instance.WorkingPath,
                Arguments        = GetArguments(sourceFile, length),
                UseShellExecute  = false,
                CreateNoWindow   = true
            };

            if (string.IsNullOrWhiteSpace(start.Arguments))
            {
                return(null);
            }

            int  elapsedTime  = 0;
            bool eventHandled = false;

            try
            {
                Process process = new Process
                {
                    StartInfo           = start,
                    EnableRaisingEvents = true
                };

                process.Exited += (sender, args) =>
                {
                    result = new PostProcessingResultEventArgs(sourceFile, length);
                    process.Dispose();
                    eventHandled = true;
                };

                process.Start();
            }
            catch (System.ComponentModel.Win32Exception)
            {
                // Some security policies don't allow execution of programs in this way
                return(null);
            }

            // Wait for Exited event, but not more than 5 seconds.
            const int SleepAmount = 100;

            while (!eventHandled)
            {
                elapsedTime += SleepAmount;
                if (elapsedTime > 5000)
                {
                    break;
                }

                Thread.Sleep(SleepAmount);
            }

            return(result);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Runs the process to optimize the images.
        /// </summary>
        /// <param name="sourceFile">The source file.</param>
        /// <param name="length">The source file length in bytes.</param>
        /// <returns>
        /// The <see cref="Task{PostProcessingResultEventArgs}"/> containing post-processing information.
        /// </returns>
        private static PostProcessingResultEventArgs RunProcess(string sourceFile, long length)
        {
            PostProcessingResultEventArgs result = null;
            ProcessStartInfo start = new ProcessStartInfo("cmd")
            {
                WindowStyle = ProcessWindowStyle.Hidden,
                WorkingDirectory = PostProcessorBootstrapper.Instance.WorkingPath,
                Arguments = GetArguments(sourceFile, length),
                UseShellExecute = false,
                CreateNoWindow = true
            };

            if (string.IsNullOrWhiteSpace(start.Arguments))
            {
                return null;
            }

            int elapsedTime = 0;
            bool eventHandled = false;

            try
            {
                Process process = new Process
                {
                    StartInfo = start,
                    EnableRaisingEvents = true
                };

                process.Exited += (sender, args) =>
                {
                    result = new PostProcessingResultEventArgs(sourceFile, length);
                    process.Dispose();
                    eventHandled = true;
                };

                process.Start();
            }
            catch (System.ComponentModel.Win32Exception)
            {
                // Some security policies don't allow execution of programs in this way
                return null;
            }

            // Wait for Exited event, but not more than 5 seconds.
            const int SleepAmount = 100;
            while (!eventHandled)
            {
                elapsedTime += SleepAmount;
                if (elapsedTime > 5000)
                {
                    break;
                }

                Thread.Sleep(SleepAmount);
            }

            return result;
        }
Exemplo n.º 5
0
        /// <summary>
        /// Runs the process to optimize the images.
        /// </summary>
        /// <param name="url">The current request url.</param>
        /// <param name="sourceFile">The source file.</param>
        /// <param name="destinationFile">The destination file.</param>
        /// <param name="length">The source file length in bytes.</param>
        /// <returns>
        /// The <see cref="System.Threading.Tasks.Task"/> containing post-processing information.
        /// </returns>
        private static PostProcessingResultEventArgs RunProcess(Uri url, string sourceFile, string destinationFile, long length)
        {
            // Create a new, hidden process to run our postprocessor command.
            // We allow no more than the set timeout (default 5 seconds) for the process to run before killing it to prevent blocking the app.
            int timeout = PostProcessorBootstrapper.Instance.Timout;
            PostProcessingResultEventArgs result = null;
            string arguments = GetArguments(sourceFile, destinationFile, length);

            if (string.IsNullOrWhiteSpace(arguments))
            {
                // Not a file we can post process.
                return(null);
            }

            ProcessStartInfo start = new ProcessStartInfo("cmd")
            {
                WindowStyle      = ProcessWindowStyle.Hidden,
                WorkingDirectory = PostProcessorBootstrapper.Instance.WorkingPath,
                Arguments        = arguments,
                UseShellExecute  = false,
                CreateNoWindow   = true
            };

            Process process = null;

            try
            {
                process = new Process
                {
                    StartInfo           = start,
                    EnableRaisingEvents = true
                };

                // Process has completed successfully within the time limit.
                process.Exited += (sender, args) =>
                {
                    result = new PostProcessingResultEventArgs(destinationFile, length);
                };

                process.Start();

                // Wait for processing to finish, but not more than our timeout.
                if (!process.WaitForExit(timeout))
                {
                    process.Kill();
                    ImageProcessorBootstrapper.Instance.Logger.Log(
                        typeof(PostProcessor),
                        $"Unable to post process image for request {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
            {
                // Make sure we always dispose and release
                process?.Dispose();
            }

            return(result);
        }
Exemplo n.º 6
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);
        }