C# 图片处理(添加水印 & 原尺寸压缩 & 等比缩放 & 创建缩略图)

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace Demo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ImageController : ControllerBase
    {
        /// <summary>
        /// 上传图片
        /// 限制jpg、png等静态图片
        /// 此方法不适用于动图处理
        /// </summary>
        /// <param name="formFile">图片文件</param>
        [HttpPost("upload")]
        [Consumes("multipart/form-data")]
        public void upload(IFormFile formFile)
        {
            string dir = Environment.CurrentDirectory;
            String savePath = dir + "\\upload\\" 
                + DateTime.Now.ToString("yyyy") + "\\" 
                + DateTime.Now.Month + "\\" 
                + DateTime.Now.Day + "\\";
            DirectoryInfo di = new DirectoryInfo(savePath);
            if(!di.Exists)
            {
                di.Create();
            }
            string fileExt = Path.GetExtension(formFile.FileName);
            // 限制图片类型
            //
            string fileFullPath = savePath + formFile.FileName;
            string zoomFilePath = savePath + "zoom_" + formFile.FileName;
            string compressFilePath = savePath + "compress_" + formFile.FileName;
            string thumbnailPath = savePath + "thumbnail_" + formFile.FileName;
            if (!System.IO.File.Exists(fileFullPath))
            {
                using (FileStream fs = System.IO.File.Create(fileFullPath))
                {
                    //保存上传文件
                    formFile.CopyTo(fs);

                    double fileSize = fs.Length / 1024;
                    if (fileSize > 300)
                    {
                        // 原尺寸压缩图片
                        // 过小的图片,用此方法压缩可能变大
                        Compress(compressFilePath, fs, 50L);
                        
                    }

                    fs.Flush();
                    fs.Close();
                }
            }
            // 等比例缩放图片
            //CompressImgProportionally(fileFullPath, zoomFilePath);


            // 文本水印
            //TxtWatermark(fileFullPath);

            // 图片水印
            //ImgWatermark(fileFullPath);

            // 缩略图
            //CreateThumbnail(fileFullPath, thumbnailPath);

            
        }

        /// <summary>
        /// 等比缩放
        /// </summary>
        /// <param name="sfile">源文件路径</param>
        /// <param name="dfile">保存路径</param>
        /// <param name="size">压缩后的图片最大大小(kb)</param>
        /// <param name="scaling">缩放比例(1~100)</param>
        /// <param name="quality">图片保存质量百分比(0~100)</param>
        /// <param name="firstInvoke">是否第一次压缩</param>
        /// <returns></returns>
        private bool CompressImgProportionally(string sfile, string dfile, int size = 50, int scaling = 50, int quality = 100, bool firstInvoke = true)
        {
            FileInfo fileInfo = new FileInfo(sfile);
            //如果是第一次调用,原始图片大小<要压缩的图片大小,则直接复制文件
            if(firstInvoke && fileInfo.Length < size * 1024)
            {
                fileInfo.CopyTo(dfile);
                return true;
            }
            if(scaling<0 || scaling>100)
            {
                scaling = 50;
            }
            if(quality < 0 || quality > 100)
            {
                quality = 100;
            }
            // 缩放百分比
            double per = 100.00 / scaling;

            Image srcImg = Image.FromFile(sfile);
            ImageFormat imageFormat = srcImg.RawFormat;
            int dHeight = (int)(srcImg.Height / per);
            int dWidth = (int)(srcImg.Width / per);
            int sw = 0, sh = 0;
            // 图片尺寸
            Size src_img_zise = new Size(srcImg.Width, srcImg.Height);
            if(src_img_zise.Width > dHeight || src_img_zise.Width > dWidth)
            {
                if((src_img_zise.Width * dHeight) > (src_img_zise.Width*dWidth))
                {
                    // 纵向图
                    sw = dWidth;
                    sh = (dWidth * src_img_zise.Height) / src_img_zise.Width;
                }
                else
                {
                    // 横向图
                    sh = dHeight;
                    sw = (src_img_zise.Width * dHeight) / src_img_zise.Height;
                }
            }
            else
            {
                sw = src_img_zise.Width;
                sh = src_img_zise.Height;
            }
            
            Bitmap bmp = new Bitmap(dWidth, dHeight);
            Graphics g = Graphics.FromImage(bmp);
            g.Clear(Color.WhiteSmoke);
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            g.DrawImage(srcImg, new Rectangle(0, 0, sw, sh), 0, 0, srcImg.Width, srcImg.Height, GraphicsUnit.Pixel);
            g.Dispose();
            //保存图片
            EncoderParameters encoderParameters = new EncoderParameters();
            long[] qlty = new long[1];
            qlty[0] = quality; 
            EncoderParameter eParameter = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qlty);
            encoderParameters.Param[0] = eParameter;
            try
            {
                ImageCodecInfo imageCodec = GetImageEncoder(imageFormat);
                
                if(imageCodec != null)
                {
                    bmp.Save(dfile, imageCodec, encoderParameters);
                    // 递归
                    // 条件:如果图片尺寸还是比较大
                    // 选项:缩放后的图片做源文件,缩放比例设为100,降低保存质量
                    
                }
                else
                {
                    bmp.Save(dfile, imageFormat);
                }
                return true;
            }
            catch
            {
                return false;
            }
            finally
            {
                srcImg.Dispose();
                bmp.Dispose();
            }
        }
        
        /// <summary>
        /// 原尺寸压缩,比较小的图片,可能压缩后反而更大!
        /// </summary>
        /// <param name="savePath">保存路径</param>
        /// <param name="imgStream">图片文件流</param>
        /// <param name="quality">压缩质量0~100,数值越大质量越高</param>
        /// <returns></returns>
        private void Compress(string savePath, Stream imgStream, long quality)
        {
            using (Image img = Image.FromStream(imgStream))
            {
                using (Bitmap bitmap = new Bitmap(img))
                {
                    ImageCodecInfo imageCodec = GetImageEncoder(img.RawFormat);
                    System.Drawing.Imaging.Encoder imgEncoder = Encoder.Quality;
                    EncoderParameters encoderParameters = new EncoderParameters();
                    EncoderParameter encoderParameter = new EncoderParameter(imgEncoder, quality);
                    encoderParameters.Param[0] = encoderParameter;
                    using (MemoryStream ms = new MemoryStream())
                    {
                        bitmap.Save(ms, imageCodec, encoderParameters);
                        encoderParameter.Dispose();
                        encoderParameters.Dispose();

                        System.IO.File.WriteAllBytes(savePath, ms.ToArray());
                    }
                }
            }
        }

        /// <summary>
        /// 图片编码信息
        /// </summary>
        /// <param name="rawFormat">格式</param>
        /// <returns></returns>
        private ImageCodecInfo GetImageEncoder(ImageFormat rawFormat)
        {
            ImageCodecInfo[] codecInfos = ImageCodecInfo.GetImageEncoders();
            foreach(ImageCodecInfo codec in codecInfos)
            {
                if(codec.FormatID == rawFormat.Guid)
                {
                    return codec;
                }
            }
            return null;
        }
        
        /// <summary>
        /// 创建缩略图
        /// </summary>
        /// <param name="srcFile">原图路径</param>
        /// <param name="destFile">缩略图保存路径</param>
        /// <param name="thumbWidth">缩略图-宽</param>
        /// <param name="thumbHight">缩略图-高</param>
        private void CreateThumbnail(string srcFile, string destFile, int thumbWidth = 300, int thumbHight = 300)
        {
            using (var img = new Bitmap(srcFile))
            {
                Size originSize = new Size(img.Width, img.Height);
                Size targetSize = new Size(thumbWidth, thumbHight);
                targetSize = ThumbnailSize(originSize, targetSize);
                using (var bmp = new Bitmap(targetSize.Width,targetSize.Height,PixelFormat.Format32bppArgb))
                {
                    using (Graphics g = Graphics.FromImage(bmp))
                    {
                        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
                        using (var wrapmode = new ImageAttributes())
                        {
                            wrapmode.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
                            g.DrawImage(img, new Rectangle(0, 0, targetSize.Width, targetSize.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, wrapmode);
                        }
                    }
                    bmp.Save(destFile);
                }
            }
        }

        /// <summary>
        /// 缩略图尺寸
        /// </summary>
        /// <param name="originSize">原图尺寸</param>
        /// <param name="targetSize">缩略图默认尺寸</param>
        /// <returns></returns>
        private Size ThumbnailSize(Size originSize, Size targetSize)
        {
            if(originSize.Width<=targetSize.Width && originSize.Height<=targetSize.Height)
            {
                return originSize;
            }
            double targetRatio = targetSize.Width / targetSize.Height;
            double originRatio = originSize.Width / originSize.Height;
            var size = new Size(targetSize.Width, targetSize.Height);
            if(originRatio<targetRatio)
            {
                size.Width = (originSize.Width * targetSize.Height) / originSize.Height;
            }
            else
            {
                size.Height = (originSize.Height * targetSize.Width) / originSize.Width;
            }
            return size;
        }

        /// <summary>
        /// 文字水印
        /// </summary>
        /// <param name="imgSrc">图片地址</param>
        /// <param name="watermarkPos">水印位置</param>
        /// <param name="watermark">水印文字</param>
        /// <param name="fontName">字体</param>
        /// <param name="fontSize">字体大小</param>
        /// <param name="opacity">不透明度(0~255),值越大越不透明^_^</param>
        /// <param name="quality">图片保存质量(0~100)</param>
        private void TxtWatermark(string imgSrc, int watermarkPos = 9, string watermark = "cnblogs.com/falltakeman",string fontName = "微软雅黑", float fontSize = 14.5f,int opacity = 23,int quality = 100)
        {
            string dir = Path.GetDirectoryName(imgSrc);
            string fName = Path.GetFileName(imgSrc);
            string imgDest = Path.Combine(dir, "txtWatermark_" + fName);
            
            byte[] imgBytes = System.IO.File.ReadAllBytes(imgSrc);
            Image img = Image.FromStream(new MemoryStream(imgBytes));
            Graphics imgGraph = null;
            // 如果从具有索引像素格式的图片提取合成图,会抛出异常。
            // see at https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.graphics.fromimage?view=netframework-4.8
            if (isPixelIndexed(img.PixelFormat))
            {
                Bitmap bmp = new Bitmap(img.Width, img.Height, PixelFormat.Format32bppArgb);
                imgGraph = Graphics.FromImage(bmp);
                imgGraph.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                imgGraph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                imgGraph.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                imgGraph.DrawImage(img, 0, 0);

                SaveTextWatermark(imgGraph, bmp, imgDest, watermarkPos, watermark, fontName, fontSize, opacity, quality);

            }
            else
            {
                imgGraph = Graphics.FromImage(img);
                SaveTextWatermark(imgGraph, img, imgDest, watermarkPos, watermark, fontName, fontSize, opacity, quality);
            }
            
            imgGraph.Dispose();
            img.Dispose();

        }

        /// <summary>
        /// 图片水印
        /// </summary>
        /// <param name="imgSrc">要加水印的原图(本地路径)</param>
        /// <param name="watermarkImgPath">水印图片路径</param>
        /// <param name="watermarkPos">水印位置</param>
        /// <param name="quality">保存图片质量(0~100)</param>
        /// <param name="watermarkImgTransparency">水印图片透明度(1~10)</param>
        private void ImgWatermark(string imgSrc, string watermarkImgPath = "", int watermarkPos = 1, int quality = 100, int watermarkImgTransparency = 7)
        {
            byte[] imageBytes = System.IO.File.ReadAllBytes(imgSrc);
            Image img = Image.FromStream(new MemoryStream(imageBytes));
            
            string dir = Path.GetDirectoryName(imgSrc);
            string fName = Path.GetFileName(imgSrc);
            string imgDest = Path.Combine(dir, "imgWatermark_" + fName);

            watermarkImgPath = string.IsNullOrWhiteSpace(watermarkImgPath) ? Environment.CurrentDirectory + "\\Asset\\watermark\\octocat.png" : watermarkImgPath;
            Graphics imgGraph = null;
            // 如果从具有索引像素格式的图片提取合成图,会抛出异常。
            // see at https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.graphics.fromimage?view=netframework-4.8
            if (isPixelIndexed(img.PixelFormat))
            {
                // 如果原图具有索引像素格式,将图像换一种格式,以去除索引像素格式
                Bitmap bmp = new Bitmap(img.Width, img.Height, PixelFormat.Format32bppArgb);
                // 使用新格式的合成图
                imgGraph = Graphics.FromImage(bmp);
                // 与合成图关联的插补模式
                imgGraph.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                // 合成图的呈现质量
                imgGraph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                // 合成图质量
                imgGraph.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                // 先将原图画在合成图上
                imgGraph.DrawImage(img, 0, 0);

                SaveImgWatermark(imgGraph, bmp, watermarkImgPath, imgDest, watermarkPos, quality, watermarkImgTransparency);
            }
            else
            {
                imgGraph = Graphics.FromImage(img);
                imgGraph.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
                imgGraph.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                imgGraph.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;

                SaveImgWatermark(imgGraph, img, watermarkImgPath, imgDest, watermarkPos, quality, watermarkImgTransparency);
            }

            imgGraph.Dispose();
            img.Dispose();
            
        }

        /// <summary>
        /// 保存图片文字水印
        /// </summary>
        /// <param name="imgGraph">合成图</param>
        /// <param name="img">原图</param>
        /// <param name="imgDest">保存路径</param>
        /// <param name="watermarkPos">水印位置</param>
        /// <param name="watermark">水印文本内容</param>
        /// <param name="fontName">字体</param>
        /// <param name="fontSize">字体大小</param>
        /// <param name="opacity">不透明度(0~255)</param>
        /// <param name="quality">保存质量</param>
        private void SaveTextWatermark(Graphics imgGraph,Image img,string imgDest, int watermarkPos, string watermark,string fontName,float fontSize,int opacity,int quality)
        {
            
            Font font = new Font(fontName, fontSize, FontStyle.Regular, GraphicsUnit.Pixel);
            SizeF size = imgGraph.MeasureString(watermark, font);
            float x = 0, y = 0;
            switch (watermarkPos)
            {
                case 1: // 左上
                    x = (float)img.Width * (float).01;
                    y = (float)img.Height * (float).01;
                    break;
                case 2: // 中上
                    x = ((float)img.Width * (float).50) - (size.Width / 2);
                    y = (float)img.Height * (float).01;
                    break;
                case 3: // 右上
                    x = ((float)img.Width * (float).99) - size.Width;
                    y = (float)img.Height * (float).01;
                    break;
                case 4: // 左中
                    x = (float)img.Width * (float).01;
                    y = ((float)img.Height * (float).50) - (size.Height / 2);
                    break;
                case 5: // 居中
                    x = ((float)img.Width * (float).50) - (size.Width / 2);
                    y = ((float)img.Height * (float).50) - (size.Height / 2);
                    break;
                case 6: // 右中
                    x = ((float)img.Width * (float).99) - size.Width;
                    y = ((float)img.Height * (float).50) - (size.Height / 2);
                    break;
                case 7: // 左下
                    x = (float)img.Width * (float).01;
                    y = ((float)img.Height * (float).99) - size.Height;
                    break;
                case 8: // 中下
                    x = ((float)img.Width * (float).50) - (size.Width / 2);
                    y = ((float)img.Height * (float).99) - size.Height;
                    break;
                case 9: // 右下
                    x = ((float)img.Width * (float).99) - size.Width;
                    y = ((float)img.Height * (float).99) - size.Height;
                    break;
            }
            if (opacity < 0 || opacity > 256)
            {
                opacity = 255;
            }
            // 中国色 http://zhongguose.com
            //Brush snowWhite = new SolidBrush(Color.FromArgb(255, 254, 149));
            //Brush rosePink = new SolidBrush(Color.FromArgb(248, 179, 127));
            Brush strawberryRed = new SolidBrush(Color.FromArgb(opacity, 239, 111, 72));
            imgGraph.DrawString(watermark, font, new SolidBrush(Color.White), x, y);
            imgGraph.DrawString(watermark, font, strawberryRed, x + 1, y + 1);
            
            EncoderParameters encoderParameters = new EncoderParameters();
            long[] qualityParam = new long[1];
            if (quality <= 0 || quality > 100)
            {
                quality = 100;
            }
            qualityParam[0] = quality;
            EncoderParameter parameter = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qualityParam);
            encoderParameters.Param[0] = parameter;
            ImageCodecInfo ici = GetImageEncoder(img.RawFormat);
            if (ici != null)
            {
                img.Save(imgDest, ici, encoderParameters);
            }
            else
            {
                img.Save(imgDest);
            }
        }

        /// <summary>
        /// 保存图片水印
        /// </summary>
        /// <param name="imgGraph">合成图</param>
        /// <param name="img">原图</param>
        /// <param name="watermarkImgPath">图片水印路径</param>
        /// <param name="imgDest">保存路径</param>
        /// <param name="watermarkPos">水印位置</param>
        /// <param name="quality">保存质量</param>
        /// <param name="watermarkImgTransparency">水印透明度</param>
        private void SaveImgWatermark(Graphics imgGraph,Image img,string watermarkImgPath,string imgDest,int watermarkPos,int quality,int watermarkImgTransparency)
        {
            // 水印图片
            Image watermark = new Bitmap(watermarkImgPath);

            if (watermark.Height >= img.Height || watermark.Width >= img.Width)
                return;
            // 图片属性
            ImageAttributes imageAttributes = new ImageAttributes();
            ColorMap colorMap = new ColorMap();
            colorMap.OldColor = Color.FromArgb(255, 0, 255, 0);
            colorMap.NewColor = Color.FromArgb(0, 0, 0, 0);
            ColorMap[] remapTable = { colorMap };
            imageAttributes.SetRemapTable(remapTable, ColorAdjustType.Bitmap);
            // 水印图片透明度
            float transparency = 0.5F;
            if (watermarkImgTransparency >= 1 && watermarkImgTransparency <= 10)
                transparency = (watermarkImgTransparency / 10.0F);


            float[][] colorMatrixElements = {
                                                new float[] {1.0f,  0.0f,  0.0f,  0.0f, 0.0f},
                                                new float[] {0.0f,  1.0f,  0.0f,  0.0f, 0.0f},
                                                new float[] {0.0f,  0.0f,  1.0f,  0.0f, 0.0f},
                                                new float[] {0.0f,  0.0f,  0.0f,  transparency, 0.0f},
                                                new float[] {0.0f,  0.0f,  0.0f,  0.0f, 1.0f}
                                            };
            // 颜色矩阵
            ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);

            imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

            // 水印位置
            // 用枚举!!
            int x = 0, y = 0;
            switch (watermarkPos)
            {
                case 1: // 左上
                    x = (int)(img.Width * (float).01);
                    y = (int)(img.Height * (float).01);
                    break;
                case 2: // 中上
                    x = (int)((img.Width * (float).50) - (watermark.Width / 2));
                    y = (int)(img.Height * (float).01);
                    break;
                case 3: // 右上
                    x = (int)((img.Width * (float).99) - (watermark.Width));
                    y = (int)(img.Height * (float).01);
                    break;
                case 4: // 左中
                    x = (int)(img.Width * (float).01);
                    y = (int)((img.Height * (float).50) - (watermark.Height / 2));
                    break;
                case 5: // 居中
                    x = (int)((img.Width * (float).50) - (watermark.Width / 2));
                    y = (int)((img.Height * (float).50) - (watermark.Height / 2));
                    break;
                case 6: // 右中
                    x = (int)((img.Width * (float).99) - (watermark.Width));
                    y = (int)((img.Height * (float).50) - (watermark.Height / 2));
                    break;
                case 7: // 左下
                    x = (int)(img.Width * (float).01);
                    y = (int)((img.Height * (float).99) - watermark.Height);
                    break;
                case 8: // 中下
                    x = (int)((img.Width * (float).50) - (watermark.Width / 2));
                    y = (int)((img.Height * (float).99) - watermark.Height);
                    break;
                case 9: // 右下
                    x = (int)((img.Width * (float).99) - (watermark.Width));
                    y = (int)((img.Height * (float).99) - watermark.Height);
                    break;
            }
            // 画上水印图片
            imgGraph.DrawImage(watermark, new Rectangle(x, y, watermark.Width, watermark.Height), 0, 0, watermark.Width, watermark.Height, GraphicsUnit.Pixel, imageAttributes);

            EncoderParameters encoderParams = new EncoderParameters();
            long[] qualityParam = new long[1];
            if (quality < 0 || quality > 100)
            {
                quality = 100;
            }
            qualityParam[0] = quality;
            EncoderParameter encoderParam = new EncoderParameter(Encoder.Quality, qualityParam);
            encoderParams.Param[0] = encoderParam;

            ImageCodecInfo ici = GetImageEncoder(img.RawFormat);
            if (ici != null)
            {
                img.Save(imgDest, ici, encoderParams);
            }
            else
            {
                img.Save(imgDest);
            }

            watermark.Dispose();
            imageAttributes.Dispose();

        }

        /// <summary>
        /// 会引发异常的索引格式
        /// </summary>
        private static PixelFormat[] pixelFormats = 
        {
            PixelFormat.Undefined,
            PixelFormat.DontCare,
            PixelFormat.Format1bppIndexed,
            PixelFormat.Format4bppIndexed,
            PixelFormat.Format8bppIndexed,
            PixelFormat.Format16bppGrayScale,
            PixelFormat.Format16bppArgb1555
        };

        /// <summary>
        /// 是否包含索引格式
        /// </summary>
        /// <param name="format">格式</param>
        /// <returns></returns>
        private bool isPixelIndexed(PixelFormat format)
        {
            foreach (PixelFormat pf in pixelFormats)
            {
                if (pf.Equals(format)) return true;
            }
            return false;
        }


    }


}

 

   水印效果图:

posted @ 2022-03-17 15:15  原来是李  阅读(965)  评论(0编辑  收藏  举报