C# Winform版批量压缩图片程序

需求

上周,领导给我分配了一个需求:服务器上的图片文件非常大,每天要用掉两个G的大小的空间,要做一个自动压缩图片的工具处理这些大图片。领导的思路是这样的:

1)打开一个图片,看它的属性里面象素是多少,大于1000就按比例缩小到1000。

2)再看它的品质属性,比如我们标准是50,如果大于这个值再修改品质。

压缩后的文件大小不能超过200k。

思路

因为服务器上的图片文件名是加密处理过的,和图片文件一起存在的还有其它附件,没有后缀名,用肉眼根本看不出来是否是图片文件。所以刚开始的时候,我的思路是先批量修改后缀名,再获取图片的像素,最后再进行压缩。后来在做的过程中,发现不用处理后缀名,直接获取图片信息就能识别文件是否是图片。

所以,最后的做法是:

1)遍历文件夹下的图片文件的时候,先根据图片信息,把图片文件提取到列表

2)然后再根据图片的像素大小进行处理。像素在1000以内的直接修改图片品质处理,像素大于1000的按尺寸大小压缩图片,然后再修改图片品质处理。

(像素大于1000这种情况之所以有两步是因为按尺寸大小进行压缩后,图片大小大于1M,不符合预期的要求,所以压缩图片后再修改图片品质。这一步为了避免混淆,我把按尺寸大小压缩图片放到另一个文件夹处理,这个文件夹在处理好图片后,会把压缩图片文件进行删除,所以这个文件夹永远是空的,不会占空间)

//做的时候一听到是自动压缩图片,批量处理文件,以为很难,很深奥,真正动手后其实是办法总比困难多。总有办法实现的,只是时间问题。

代码片段

1)因为文件的位置不固定,文件夹下面有图片,也有文件夹,里面还有图片。所以要遍历子目录。

        /// <summary>
        /// 遍历文件
        /// </summary>
        /// <param name="di"></param>
        public void ListFiles(DirectoryInfo di)
        {
            if (!di.Exists)
            {
                return;
            }

            if (di == null)
            {
                return;
            }

            //返回当前目录的文件列表
            FileInfo[] files = di.GetFiles();

            for (int i = 0; i < files.Length; i++)
            {

                try
                {
                    //判断是否具有照片信息,报错即不是照片文件
                    GetMetaData.GetExifByMe(files[i].FullName);

                    //把图片文件添加到列表视图
                    this.lvSourceFolderList.Items.Add(files[i].FullName);

                    //把图片文件添加到图片列表
                    imageList.Add(files[i].FullName);

                }
                catch (Exception)
                {
                    //Logging.Error(System.IO.Path.GetFileName(files[i].FullName) + ",非图片文件," + ex.Message);
                    continue;
                }

            }
            this.lbInfomation.Text = "共" + this.lvSourceFolderList.Items.Count + "条数据";
            //返回当前目录的子目录
            DirectoryInfo[] dis = di.GetDirectories();

            for (int j = 0; j < dis.Length; j++)
            {
                // Console.WriteLine("目录:" + dis[j].FullName);
                ListFiles(dis[j]);//对于子目录,进行递归调用
            }

        }

2)判断图片是否具有照片信息,我用的是MetadataExtractor,直接在nuget里面添加安装好,再添加一个GetExifByMe即可。这里在调用GetExifByMe的时候,不是图片文件会报错,报错的我直接忽略,继续continue。

  //判断是否具有照片信息,报错即不是照片文件
   GetMetaData.GetExifByMe(files[i].FullName);
        #region   通过metadata-extractor获取照片参数

        //参考文献
        //官网: https://drewnoakes.com/code/exif/
        //nuget 官网:https://www.nuget.org/
        //nuget 使用: http://www.cnblogs.com/chsword/archive/2011/09/14/NuGet_Install_OperatePackage.html
        //nuget MetadataExtractor: https://www.nuget.org/packages/MetadataExtractor/

        /// <summary>通过MetadataExtractor获取照片参数
        /// </summary>
        /// <param name="imgPath">照片绝对路径</param>
        /// <returns></returns>
        public static Dictionary<string, string> GetExifByMe(string imgPath)
        {
            var rmd = ImageMetadataReader.ReadMetadata(imgPath);

            var rt = new Dictionary<string, string>();
            foreach (var rd in rmd)
            {
                foreach (var tag in rd.Tags)
                {
                    var temp = EngToChs(tag.Name);
                    if (temp == "其他")
                    {
                        continue;
                    }
                    if (!rt.ContainsKey(temp))
                    {
                        rt.Add(temp, tag.Description);
                    }

                }
            }
            return rt;
        }

        /// <summary>筛选参数并将其名称转换为中文
        /// </summary>
        /// <param name="str">参数名称</param>
        /// <returns>参数中文名</returns>
        private static string EngToChs(string str)
        {
            var rt = "其他";
            switch (str)
            {
                case "Exif Version":
                    rt = "Exif版本";
                    break;
                case "Model":
                    rt = "相机型号";
                    break;
                case "Lens Model":
                    rt = "镜头类型";
                    break;
                case "File Name":
                    rt = "文件名";
                    break;
                case "File Size":
                    rt = "文件大小";
                    break;
                case "Date/Time":
                    rt = "拍摄时间";
                    break;
                case "File Modified Date":
                    rt = "修改时间";
                    break;
                case "Image Height":
                    rt = "照片高度";
                    break;
                case "Image Width":
                    rt = "照片宽度";
                    break;
                case "X Resolution":
                    rt = "水平分辨率";
                    break;
                case "Y Resolution":
                    rt = "垂直分辨率";
                    break;
                case "Color Space":
                    rt = "色彩空间";
                    break;

                case "Shutter Speed Value":
                    rt = "快门速度";
                    break;
                case "F-Number":
                    rt = "光圈";//Aperture Value也表示光圈
                    break;
                case "ISO Speed Ratings":
                    rt = "ISO";
                    break;
                case "Exposure Bias Value":
                    rt = "曝光补偿";
                    break;
                case "Focal Length":
                    rt = "焦距";
                    break;

                case "Exposure Program":
                    rt = "曝光程序";
                    break;
                case "Metering Mode":
                    rt = "测光模式";
                    break;
                case "Flash Mode":
                    rt = "闪光灯";
                    break;
                case "White Balance Mode":
                    rt = "白平衡";
                    break;
                case "Exposure Mode":
                    rt = "曝光模式";
                    break;
                case "Continuous Drive Mode":
                    rt = "驱动模式";
                    break;
                case "Focus Mode":
                    rt = "对焦模式";
                    break;
            }
            return rt;
        }

        #endregion

文件浏览完毕后的截图:

3)文件全部浏览完毕后,就开始进行压缩。

因为文件数量大,原来的简单压缩版本总是容易卡死,这里的新版本用了线程,就没有卡死的问题了。

这里的压缩核心代码直接参考了
用C#开发一个WinForm版的批量图片压缩工具

      Thread workThread = new Thread(new ThreadStart(CompressAll));
      workThread.IsBackground = true;
      workThread.Start();

我添加了i标识处理成功的文件数量,压缩失败的时候i-=1。


if (CompressPicture(item, fileName))
{
    if (this.InvokeRequired)
    {
        this.Invoke(new DelegateWriteResult(WriteResult), new object[] { item, true });
     }
     else
    {
       this.WriteResult(item, true);
    }
}
 else
{
    i -= 1;

    if (this.InvokeRequired)
    {
        this.Invoke(new DelegateWriteResult(WriteResult), new object[] { item, false });
    }
     else
    {
        this.WriteResult(item, false);
    }
}

改变图片质量这里就是第二步思路,分两步走:

像素在1000以内的直接修改图片品质处理;

像素大于1000的按尺寸大小压缩图片,然后再修改图片品质处理。

        /// <summary>
        /// 改变图片质量
        /// </summary>
        /// <param name="imgPath">文件路径</param>
        /// <param name="imgName">文件名</param>
        private static bool VaryQualityLevel(string imgPath, string imgName)
        {

            bool result = false;

            Bitmap bmp1 = new Bitmap(imgPath);

            //获取照片信息
            // GetExifByMe(imgPath);
            //先获取图片的像素
            var imgPixl = RGB2Gray(bmp1);

            //像素超出,先压缩图片
            if (imgPixl.Width > 1000 && imgPixl.Height > 1000)
            {
                double width = 0;
                double height = 0;
                if (imgPixl.Width > 2000 && imgPixl.Height > 2000)
                {
                    width = System.Math.Ceiling(Convert.ToDouble(imgPixl.Width / 4));
                    height = System.Math.Ceiling(Convert.ToDouble(imgPixl.Height / 4));
                }
                else if (imgPixl.Width > 1000 && imgPixl.Height > 1000)
                {
                    width = System.Math.Ceiling(Convert.ToDouble(imgPixl.Width / 2));
                    height = System.Math.Ceiling(Convert.ToDouble(imgPixl.Height / 2));
                }
                //cutimg先创建好
                //检查是否存在文件夹
                string subPath = @"d:/cutimg/";
                if (false == System.IO.Directory.Exists(subPath))
                {
                    //创建pic文件夹
                    System.IO.Directory.CreateDirectory(subPath);
                }
                result = FixSize(imgPath, Convert.ToInt32(width), Convert.ToInt32(height), subPath + imgName, imgName);
            }
            else
            {

                result = SetImgQuality(imgPath, imgPath, imgName);

            }

            return result;

        }

调用按图片尺寸压缩方法,先存储压缩后的图片,图片大小往往还超过1M。再设置图片的质量,二次处理,压缩后的图片大小小于200K。

        /// <summary> 按图片尺寸大小压缩图片</summary>
        /// <param name="sourceFile">原始图片文件</param>
        /// <param name="xWidth">图片width</param>
        ///  <param name="yWidth">图片height</param>
        /// <param name="outputFile">输出文件名</param>
        ///  <param name="imgName">文件名</param>
        /// <returns>成功返回true,失败则返回false</returns>
        public static bool FixSize(string sourceFile, int xWidth, int yWidth, string outputFile, string imgName)
        {
            try
            {
                Bitmap sourceImage = new Bitmap(sourceFile);

                ImageCodecInfo myImageCodecInfo = GetEncoderInfo("image/jpeg");

                Bitmap newImage = new Bitmap((int)(xWidth), (int)(yWidth));

                Graphics g = Graphics.FromImage(newImage);

                g.DrawImage(sourceImage, 0, 0, xWidth, yWidth);

                sourceImage.Dispose();

                g.Dispose();

                newImage.Save(outputFile);

                //设置图片质量
                SetImgQuality(sourceFile, outputFile, imgName);

                newImage.Dispose();

                //删除该图片文件
                File.Delete(outputFile);

                return true;
            }
            catch (Exception ex)
            {
                Logging.Error("FixSize:" + imgName + "  压缩出错:" + ex.Message);
                return false;
            }
        }

调用按图片尺寸压缩的时候,这里发生“GDI+发生一般性错误”这个提示,原因是因为调用了SetImgQuality这个方法,文件还没有释放出来,在最后加上bmp1.Dispose();就解决了。

 //设置图片质量
 SetImgQuality(sourceFile, outputFile, imgName);
 

在文件压缩出错的时候,我把出错的文件写入文本:


                for (int j = 0; j < this.lvSourceFolderList.Items.Count; j++)
                {
                    if (fileName == this.lvSourceFolderList.Items[j].Text)
                    {
                        //压缩失败的文件写入文本
                        using (StreamWriter my_writer = new StreamWriter(@"d:\CompressFailFile.txt", true, System.Text.Encoding.Default))
                        {
                            string txtstr = "压缩失败:" + fileName + "\r\n";
                            my_writer.Write(txtstr);
                            my_writer.Flush();
                        }

                        this.lvSourceFolderList.Items[j].BackColor = SystemColors.ControlDark;
                    }
                }

在这里出现“文件正由另一进程使用,该进程无法访问该文件”的错误提示,当时在本地上跑没有任何问题,放在服务器上跑就报错。后来把服务器上面的文件拿到本地测试,发现是这里出错了。换了using后完美解决。

压缩出错的文件除了在文本记录外,我还做了高亮显示。选中高亮数据的时候,因为无法复制,添加了SelectedIndexChanged事件以及文本框显示。

        /// <summary>
        /// 选择行
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lvSourceFolderList_SelectedIndexChanged(object sender, EventArgs e)
        {

            ListView.SelectedIndexCollection indexes = lvSourceFolderList.SelectedIndices;//

            string pr = "";

            foreach (int index in indexes)
            {
                pr = lvSourceFolderList.Items[index].Text;
            }

            this.lblChoose.Visible = true;
            this.txtContent.Visible = true;
            this.txtContent.Text = pr;// 显示选择的行的内容

        }

最后,贴上我的源码。因为自己在做的过程中参考、借鉴了很多前辈的分享,我也把自己完整的代码分享出来。

源码 Github地址

参考资源

在做的过程中,我走了很多弯路,幸好在这个互联网发达的时代,在知识共享的时代,我有幸参考了各路前辈分享的资料,才得以完成这个任务。非常感谢以下前辈的分享,还有一个分享当时没有保存到链接,找不着了。无论如何,我心永存感激。

C#保存图片设置图片质量的方法

.net c#通过Exif获取图片信息(参数)

用C#开发一个WinForm版的批量图片压缩工具

原文地址:https://www.cnblogs.com/mooncake-wong/p/11163824.html

时间: 2024-10-31 20:35:28

C# Winform版批量压缩图片程序的相关文章

这是一款可以批量压缩图片的软件

很多小伙伴都在找一款图片压缩的软件,很多的软件一次只能压缩一张图片,小编最近遇到了一个神奇的压缩软件,这是一款可以批量压缩图片的软件,下面就是小编分享的图片压缩软件的使用方法哦,可以来看看!工具介绍:迅捷压缩软件1:打开电脑上的这个图片压缩软件,需要压缩图片,我们就可以点击页面中的图片压缩.2:进入操作界面之后点击添加文件或者添加文件夹的按钮,把需要进行压缩的图片添加进操作界面中.一次可以添加多张图片然后同时进行压缩.3:选择一个合适的压缩选项,添加文件的下面有压缩选项和输出格式可以选择.根据自

电脑如何批量压缩图片

电脑压缩图片的方法很简单,只不过还得借助到压缩的工具,借助压缩工具的强大的压缩功能,在几秒钟就可以实现视频的压缩,方法还是简单的,电脑如何批量压缩图片?具体方法一起来学习!参照下面的方法压缩图片:图片压缩可以借助压缩软件1:打开电脑上的图片压缩软件,页面有三个压缩的功能,点击图片压缩就可以进入操作界面.2:添加要进行压缩的图片,点击添加文件或者添加文件夹的按钮就可以选择文件,一次可以添加多张图片同时进行压缩.3:在添加文件下面有输出格式以及压缩选项的设置,根据自己的需要进行选择.4:压缩好的图片

winform采集网站美女图片程序---多线程篇

之前做的采集程序,  是单线程的,  一个图片列表, 要等着一一采完....浪费了不少时间,  正好今天家里带宽升级, 可以使用多线程采集了.... 连夜改进原来的程序.  使用多线程去采集.... 设定思路: 采集目标: http://www.8kmm.com,   已知网址列表(List保存),  应用多线程(Thread)读取该列表, 获取url时不能重复(加锁Lock). 允许无序采集! 先放个美女提提神! 多线程核心代码: 1 #region 全局变量 2 //线程列表 3 List<

如何将图片批量压缩?全面盘点这几种小方法

写在前面:现在的人们都喜欢用自己手机里的相机来记录自己生活中的点滴美好,而拍照成为人们记录自己生活的最常用的一个方式,这样当时间过去也会记得自己之前曾去过哪些地方,可是照片多了之后也很烦恼,图片太多,就容易出现体积过大的,在传输的时候特别麻烦,这就需要将图片进行压缩,如果图片过多,就需要进行批量压缩,那么如何将图片批量压缩?今天就来为大家爱全面盘点这几种小方法.一.软件压缩推荐指数:★★★★★借助工具:迅捷压缩软件介绍:迅捷压缩软件是一款操作简单,快捷高效的图片压缩,视频压缩,PDF压缩软件,能

用Photoshop软件实现批量压缩照片

前提:手头有 "大" 照片,出于某种原因想把它变成 "小" 照片:电脑刚好安装有PS软件. 需知:如果您的压缩需求仅限于降低图片品质,降低图片像素,那么建议您采取方案一,否则采取方案二. 两种方案均在PScc2018上正确运行. 方案一:使用PS软件自带的脚本 文件=>脚本=>图像处理器 选择要处理的图像:勾选并选择您的文件夹 选择位置以存储处理的图像:勾选并选择您的文件夹 文件类型:修改您希望的品质(0--12)和像素 例如: 点击运行即可开始压缩 方

java上传图片并压缩图片大小

Thumbnailator 是一个优秀的图片处理的Google开源Java类库.处理效果远比Java API的好.从API提供现有的图像文件和图像对象的类中简化了处理过程,两三行代码就能够从现有图片生成处理后的图片,且允许微调图片的生成方式,同时保持了需要写入的最低限度的代码量.还支持对一个目录的所有图片进行批量处理操作.支持的处理操作:图片缩放,区域裁剪,水印,旋转,保持比例.另外值得一提的是,Thumbnailator至今仍不断更新,怎么样,感觉很有保障吧!Thumbnailator官网:h

异想家博客图片批量压缩程序

为了方便给自己的博客配图,用Golang写了一个脚本处理,现分享出来,有需要的朋友也可以参考修改使用. 压缩规则 1.图片都等比例压缩,不破坏长宽比. 2.如果是横屏图片,压缩到宽度为1280,高度适应. 3.如果是竖屏图片,压缩到高度为1000,宽度适应. 4.如果分辨率小于这个,不压缩. 5.支持png.jpg.jpeg. 使用方法 go build jfzBlogPicCompress.go 原图放在当前目录下的raw目录中,压缩后的图片生成在thumb目录下,运行生成的二进制文件即可,压

shell 批量压缩指定文件夹及子文件夹内图片

shell 批量压缩指定文件夹及子文件夹内图片 用户上传的图片,一般都没有经过压缩,造成空间浪费.因此须要编写一个程序,查找文件夹及子文件夹的图片文件(jpg,gif,png),将大于某值的图片进行压缩处理. 代码例如以下: #!/bin/bash # 查找文件夹及子文件夹的图片文件(jpg,gif,png),将大于某值的图片进行压缩处理 # Config folderPath='/home/fdipzone/photo' # 图片文件夹路径 maxSize='1M' # 图片尺寸同意值 max

利用PhotoShop批量压缩和转换图片格式

[摘要] 从单反相机中导出的照片,为保证照片质量,方便后期调整,摄影爱好者经常会在相机中将照片格式设置为.NEF,现在想转换为JPEG的格式,一张张处理太麻烦,有没有批量处理的方法?强大的PhotoShop可以帮我们自动搞定. [正文] NEF格式照片的调整方式 单反相机照相,为方便后期调整,可以将照片格式设置为NEF格式,这样如果以photoshop方式打开,自动就出现照片处理对话框,如图所示: 右边弹出的调整面板,可以方便调整曝光度和对比度等. 但如果想把照片放到手机内,或者处理好的照片希望