0

There is a requirement to optimize images uploaded to a azure blob storage. I'm writing an azure function app (blob trigger) which will take the uploaded image and compress it using a image compressor library and then save the resulting image into another blob.

I've created a custom library to compress images by referencing the ImageOptimizerWebJob library. The compression logic runs appropriate image compressor exe file (pingo.exe, cjpeg.exe, jpegtran.exe or gifsicle.exe) to compress the given image.

public CompressionResult CompressFile(string fileName, bool lossy)
    {
        string targetFile = Path.ChangeExtension(Path.GetTempFileName(), Path.GetExtension(fileName));

        ProcessStartInfo start = new ProcessStartInfo("cmd")
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            WorkingDirectory = _cwd,
            Arguments = GetArguments(fileName, targetFile, lossy),
            UseShellExecute = false,
            CreateNoWindow = true,
        };

        var stopwatch = Stopwatch.StartNew();

        using (var process = Process.Start(start))
        {
            process.WaitForExit();
        }

        stopwatch.Stop();

        return new CompressionResult(fileName, targetFile, stopwatch.Elapsed);
    }


 private static string GetArguments(string sourceFile, string targetFile, bool lossy)
    {
        if (!Uri.IsWellFormedUriString(sourceFile, UriKind.RelativeOrAbsolute) && !File.Exists(sourceFile))
            return null;

        string ext;

        try
        {
            ext = Path.GetExtension(sourceFile).ToLowerInvariant();
        }
        catch (ArgumentException ex)
        {
            Console.WriteLine(ex);
            return null;
        }

        switch (ext)
        {
            case ".png":
                File.Copy(sourceFile, targetFile);

                if (lossy)
                    return string.Format(CultureInfo.CurrentCulture, "/c pingo -s8 -q -palette=79 \"{0}\"", targetFile);
                else
                    return string.Format(CultureInfo.CurrentCulture, "/c pingo -s8 -q \"{0}\"", targetFile);

            case ".jpg":
            case ".jpeg":
                if (lossy)
                {
                    return string.Format(CultureInfo.CurrentCulture, "/c cjpeg -quality 80,60 -dct float -smooth 5 -outfile \"{1}\" \"{0}\"", sourceFile, targetFile);
                }

                return string.Format(CultureInfo.CurrentCulture, "/c jpegtran -copy none -optimize -progressive -outfile \"{1}\" \"{0}\"", sourceFile, targetFile);

            case ".gif":
                return string.Format(CultureInfo.CurrentCulture, "/c gifsicle -O3 --batch --colors=256 \"{0}\" --output=\"{1}\"", sourceFile, targetFile);
        }

        return null;
    }

My azure function uses this library to do the job. But the challenge here is, the input and output file location are azure blobs not local desktop paths.

The input and output blob data are coming from the Run() method

public static void Run([BlobTrigger("test/{name}", Connection = "")]Stream myBlob,
        [Blob("test-output/{name}", FileAccess.ReadWrite)]CloudBlockBlob output,
        string name,
        TraceWriter log)

But I cannot directly use the input and output blob paths for the image compressor. I could not figure out a way to get the output image as a memory stream (to upload to output blob)

Appreciate your suggestions to resolve this.

1
  • I'm trying an alternative way to do the same task using Magick.NET library. There I can pass input as an stream and the results can be saved to another stream. The second stream can be used to save the result image to the output blob. Commented Sep 11, 2018 at 10:25

1 Answer 1

0

You will either need to use a file system of some description or if the exe provides StdIn/Out you can do this all in a MemoryStream and save it (play with it) how you like

Here is an example for jpegoptim.exe which does support StdIn/Out

var startInfo = new ProcessStartInfo
   {
      WindowStyle = ProcessWindowStyle.Hidden,
      FileName = @"jpegoptim.exe",
      Arguments = @"-s --stdin --stdout",
      RedirectStandardOutput = true,
      RedirectStandardError = true,
      RedirectStandardInput = true,
      UseShellExecute = false,
      CreateNoWindow = true
   };

using (var process = Process.Start(startInfo))
{

   inputStream.CopyTo(process.StandardInput.BaseStream);
   process.StandardInput.Close();

   using (var file = new FileStream(dest, FileMode.Create, FileAccess.Write))
   {
      process.StandardOutput.BaseStream.CopyTo(file);
   }

   if (process.ExitCode != 0)
   {
      var message = process.StandardError.ReadToEnd();
      throw new InvalidOperationException($"Failed to optimise image : {message}");      
   }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.