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; } } }
水印效果图: