C# 圖片處理(新增水印 & 原尺寸壓縮 & 等比縮放 & 建立縮圖)
阿新 • • 發佈:2022-03-17
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")] publicvoid 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; } } }
水印效果圖: