1. 程式人生 > 實用技巧 >C#編寫批量jpg檔案壓縮小工具

C#編寫批量jpg檔案壓縮小工具

有一天,客戶領導說網站載入慢

客戶的網站請廣告公司設計的,堆疊了大量的高精度美圖。忽然有一天,老總說網站怎麼這麼慢?

客戶不懂技術,只管提需求

網站在建立的時候就已經告知會有什麼優點和弱項了,但是客戶都是善於遺忘的人。他們只管現在,不管過去。所以唯一不必的是變化這句話就又被驗證了一次。
怎麼辦呢?

  1. 全新的介面設計 ,重構網站?顯然不太現實,剛做好沒兩個月。
  2. 縮小圖片檔案大小是唯一可行可試的方案。第一步縮小時將png全部改為了jpg,因為不用考慮透明度的問題。第二步就是降低jpg圖片的質量,隨之減小檔案大小。
    tinypng.com 做了幾張,感覺太麻煩了,這要做到幾時去呢?
    於是自己寫了一個小工具,可以批量的縮小jpg檔案的大小,當然圖片質量會略有下降了,但是普通訪問者都幾乎無法注意到,是可以接受的。

為了方便,寫了一個Console應用,命令列方式執行:
Please enter resize argument.
Usage: JpgResizer -r 75 -o -b
Usage: JpgResizer *.jpg -r 75 -o -b -s
-b : 備份原始檔案,在覆蓋模式時有用 backup the original file. use when -o is set.
-o : 覆蓋模式,直接重寫原檔案 overwrite the original file.
-s : 搜尋當前目錄下的所有子目錄下的檔案 search all jpg files in sub-directories. ignore filename.
-r : 壓縮後圖片質量級別 compress level. default is 75, 100 is best.

原始碼,複製可用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace JpgResizer
{
    class Program
    {
        static bool isOverwrite = false;
        static Int64 ratio = 75L;
        static bool isIncludeSubDirectories = false;
        static bool isBackup = false;

        static void Main(string[] args)
        {
            
            string filename = "*.jpg";

            if (args.Length == 0)
            {
                Console.WriteLine("============== JPG Compress tool ==================");
                Console.WriteLine("= Author : shrek");
                Console.WriteLine("= Contact: (QQ390652)");
                Console.WriteLine("= Date   : 2021.01.21");
                Console.WriteLine("= Hangzhou Codans Cyberinfo Company. ");
                Console.WriteLine("===================================================");
                Console.WriteLine("Please enter resize argument.");
                Console.WriteLine("Usage: JpgResizer <filename> -r 75 -o -b");
                Console.WriteLine("Usage: JpgResizer *.jpg -r 75 -o -b -s");
                Console.WriteLine(" -b  : backup the original file. use when -o is set.");
                Console.WriteLine(" -o  : overwrite the original file. ");
                Console.WriteLine(" -s  : search all jpg files in sub-directories. ignore filename.");
                Console.WriteLine(" -r  : compress level. default is 75, 100 is best.");

                return;
            }

            filename = args[0];
            if (!filename.Contains(".jpg") && !filename.Contains(".jpeg") && !filename.Contains("*"))
            {
                Console.WriteLine("   missing filename.");
                return;
            }

            var index = 1;
            foreach (var item in args.Skip(1))
            {
                if (item.ToLower() == "-o")
                {
                    isOverwrite = true;
                }
                if (item.ToLower() == "-b")
                {
                    isBackup = true;
                }
                else if (item.ToLower()== "-s")
                {
                    isIncludeSubDirectories = true;
                }
                else if (item.ToLower()=="-r")
                {
                    if (args.Length >= index+1)
                    {
                        ratio = Int64.Parse(args[index + 1]);
                    }
                    else
                    {
                        Console.WriteLine("  missing ratio paramenter.");
                        return;
                    }
                }
                index++;
            }

            if (isIncludeSubDirectories)
            {
                ListJpg(AppContext.BaseDirectory);
            }
            else { 
                VaryQualityLevel(filename, ratio, isOverwrite);
            }

        }

        private static void ListJpg(string dir)
        {
            DirectoryInfo d = new DirectoryInfo(dir);
            FileInfo[] files = d.GetFiles("*.jpg");//檔案
            DirectoryInfo[] directs = d.GetDirectories();//資料夾
            foreach (FileInfo f in files)
            {
                VaryQualityLevel(f.FullName, ratio, isOverwrite);  
            }
            //獲取子資料夾內的檔案列表,遞迴遍歷  
            foreach (DirectoryInfo dd in directs)
            {
                ListJpg(dd.FullName);
            }
        }

        private static ImageCodecInfo GetEncoder(ImageFormat format)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }

        private static void VaryQualityLevel(string fileName,Int64 ratio,bool isOverwrite)
        {
            Console.WriteLine($"filename : {fileName}");
            // Get a bitmap. The using statement ensures objects  
            // are automatically disposed from memory after use.  
            var originalSize = new FileInfo(fileName).Length;
            var newSize = 0L;


            var newFileName = Path.GetFileNameWithoutExtension(fileName) + "_new" + Path.GetExtension(fileName);
            if (isOverwrite)
            {
                newFileName = fileName;
                if (isBackup)
                {
                    var backupFileName = Path.GetFileNameWithoutExtension(fileName) + "_original" + Path.GetExtension(fileName);
                    File.Copy(fileName, backupFileName, true);
                }
            }
            using (Bitmap bmp0 = new Bitmap(fileName))
            {
                var bmp1 = new Bitmap(bmp0);
                bmp0.Dispose();
                ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);

                // Create an Encoder object based on the GUID  
                // for the Quality parameter category.  
                System.Drawing.Imaging.Encoder myEncoder =
                    System.Drawing.Imaging.Encoder.Quality;

                // Create an EncoderParameters object.  
                // An EncoderParameters object has an array of EncoderParameter  
                // objects. In this case, there is only one  
                // EncoderParameter object in the array.  
                EncoderParameters myEncoderParameters = new EncoderParameters(1);

                EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, ratio);
                myEncoderParameters.Param[0] = myEncoderParameter;
                bmp1.Save(newFileName, jpgEncoder, myEncoderParameters);
                newSize = new FileInfo(newFileName).Length;
                Console.WriteLine($"  resize ok. {originalSize} -> {newSize} { ((originalSize - newSize) * 100 / originalSize).ToString("0.#") }% compressed.");
            }
        }
    }
}

總結

參考了以下資訊,站在別人的肩膀上就看得遠一些。

【日月談】是自己維護的一個微信小程式,可以線上寫日記,有好多朋友寫了快20年,您如果有興趣,可以去用用看。
https://everyday.yuelvsu.com