C# - 图片操作和Base64处理
旋转
(1)按角度旋转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | /// <summary> /// 根据角度旋转图标 /// </summary> /// <param name="img"></param> public Image RotateImg(Image img, float angle) { //通过Png图片设置图片透明,修改旋转图片变黑问题。 int width = img.Width; int height = img.Height; //角度 Matrix mtrx = new Matrix(); mtrx.RotateAt(angle, new PointF((width / 2), (height / 2)), MatrixOrder.Append); //得到旋转后的矩形 GraphicsPath path = new GraphicsPath(); path.AddRectangle( new RectangleF(0f, 0f, width, height)); RectangleF rct = path.GetBounds(mtrx); //生成目标位图 Bitmap devImage = new Bitmap(( int )(rct.Width), ( int )(rct.Height)); Graphics g = Graphics.FromImage(devImage); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; //计算偏移量 Point Offset = new Point(( int )(rct.Width - width) / 2, ( int )(rct.Height - height) / 2); //构造图像显示区域:让图像的中心与窗口的中心点一致 Rectangle rect = new Rectangle(Offset.X, Offset.Y, ( int )width, ( int )height); Point center = new Point(( int )(rect.X + rect.Width / 2), ( int )(rect.Y + rect.Height / 2)); g.TranslateTransform(center.X, center.Y); g.RotateTransform(angle); //恢复图像在水平和垂直方向的平移 g.TranslateTransform(-center.X, -center.Y); g.DrawImage(img, rect); //重至绘图的所有变换 g.ResetTransform(); g.Save(); g.Dispose(); path.Dispose(); return devImage; } |
(2)按弧度旋转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /// <summary> /// 第二种方法 /// </summary> /// <param name="b"></param> /// <param name="angle"></param> /// <returns></returns> public Image RotateImg2(Image b, float angle) { angle = angle % 360; //弧度转换 double radian = angle * Math.PI / 180.0; double cos = Math.Cos(radian); double sin = Math.Sin(radian); //原图的宽和高 int w = b.Width; int h = b.Height; int W = ( int )(Math.Max(Math.Abs(w * cos - h * sin), Math.Abs(w * cos + h * sin))); int H = ( int )(Math.Max(Math.Abs(w * sin - h * cos), Math.Abs(w * sin + h * cos))); //目标位图 Image dsImage = new Bitmap(W, H); System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(dsImage); g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; //计算偏移量 Point Offset = new Point((W - w) / 2, (H - h) / 2); //构造图像显示区域:让图像的中心与窗口的中心点一致 Rectangle rect = new Rectangle(Offset.X, Offset.Y, w, h); Point center = new Point(rect.X + rect.Width / 2, rect.Y + rect.Height / 2); g.TranslateTransform(center.X, center.Y); g.RotateTransform(360-angle); //恢复图像在水平和垂直方向的平移 g.TranslateTransform(-center.X, -center.Y); g.DrawImage(b, rect); //重至绘图的所有变换 g.ResetTransform(); g.Save(); g.Dispose(); //dsImage.Save("yuancd.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); return dsImage; } |
以上参考:基于C#的两种图片旋转方法
此外,可以直接使用已封装的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /// <summary> /// 旋转(利用已封装的方法) /// </summary> /// <param name="path">图片路径</param> /// <param name="rotateFlipType">旋转方式</param> /// <returns></returns> public bool KiRotate( string path, RotateFlipType rotateFlipType){ try { using (Bitmap bitmap = new Bitmap(path)) { // 顺时针旋转90度 RotateFlipType.Rotate90FlipNone // 逆时针旋转90度 RotateFlipType.Rotate270FlipNone // 水平翻转 RotateFlipType.Rotate180FlipY // 垂直翻转 RotateFlipType.Rotate180FlipX bitmap.RotateFlip(rotateFlipType); bitmap.Save(path); } return true ; } catch (Exception ex) { return false ; } } |
转换
(1)Bitmap & BitmapImage
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | // 程序集 System.Xaml.dll System.Drawing.dll WindowsBase.dll PresentationCore.dll // 命名空间 using System.Drawing; using System.IO; using System.Windows.Media.Imaging; using System.Windows.Interop; using System.Windows; /// <summary> /// 创建一个Bitmap对象 /// </summary> /// <param name="_uri">"../Images/test.png"</param> public Bitmap CreatBitmapObject( string _uri){ Bitmap bitmap = new Bitmap(_uri); return bitmap; } /// <summary> /// 创建一个BitmapImage对象 /// </summary> /// <param name="_uri">"../Images/test.png"</param> public BitmapImage CreatBitmapImageObject( string _uri){ BitmapImage bitmapImage = new BitmapImage( new Uri(_uri, UriKind.Relative)); return bitmapImage; } /// <summary> /// BitmapImage to Bitmap /// </summary> public Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage){ using (MemoryStream outStream = new MemoryStream()){ BitmapEncoder enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(bitmapImage)); enc.Save(outStream); Bitmap bitmap = new Bitmap(outStream); return new Bitmap(bitmap); } } /// <summary> /// Bitmap to BitmapImage /// </summary> [System.Runtime.InteropServices.DllImport( "gdi32.dll" )] public static extern bool DeleteObject(IntPtr hObject); public BitmapImage Bitmap2BitmapImage(Bitmap bitmap) { IntPtr hBitmap = bitmap.GetHbitmap(); BitmapImage retval; try { retval = (BitmapImage)Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } finally { DeleteObject(hBitmap); } return retval; } |
若是将Bitmap转换成BitmapSource,只需将返回值类型更改为BitmapSource即可。
具体参考:Converting BitmapImage to Bitmap and vice versa;
(2)Bitmap/BitmapImage & byte[]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | /// <summary> /// Bitmap to 字节数组 /// </summary> public byte [] Bitmap2Bytes(Bitmap bitmap){ ImageFormat format = bitmap.RawFormat; using (MemoryStream ms = new MemoryStream()){ bitmap.Save(ms, format); byte [] data = new byte [ms.Length]; //Save()会改变MemoryStream的Position,需要重新Seek到Begin也就是开始的0位置 ms.Seek(0, SeekOrigin.Begin); ms.Read(data, 0, Convert.ToInt32(ms.Length)); return data; } } /// <summary> /// 字节数组 to Bitmap /// </summary> public Bitmap Bytes2Bitmap( byte [] data){ MemoryStream ms = null ; try { ms = new MemoryStream(data); return new Bitmap((Image) new Bitmap(ms)); } catch (Exception ex){ throw ex; } finally { ms.Close(); } } /// <summary> /// BitmapImage to 字节数组 /// </summary> public byte [] BitmapImage2Bytes(BitmapImage bitmapImage){ byte [] bytes = null ; try { Stream ms = bitmapImage.StreamSource; if (ms != null && ms.Length > 0){ //很重要,因为Position经常位于stream的末尾,导致下面读取到的长度为0 ms.Position = 0; using (BinaryReader br = new BinaryReader(ms)){ bytes = br.ReadBytes(( int )ms.Length); } } } catch (Exception ex){ bytes = null ; throw ex; } return bytes; } /// <summary> /// 字节数组 to BitmapImage /// </summary> public BitmapImage Bytes2BitmapImage( byte [] data) { BitmapImage bitmapImage = null ; try { bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = new MemoryStream(data); bitmapImage.EndInit(); } catch (Exception ex){ bitmapImage = null ; throw ex; } return bitmapImage; } |
此时,可以总结下Bitmap、BitmapImage、Image和BitmapSource、ImageSource之间的关系
1 2 3 4 5 6 7 | // Image抽象类,Bitmap密封类 public abstract class Image: xxx public sealed class Bitmap : Image // ImageSource和BitmapSource抽象类,BitmapImage密封类 public abstract class ImageSource: xxx public abstract class BitmapSource : ImageSource public sealed class BitmapImage : BitmapSource |
压缩
图片大小 > 1M 时,可以对图片进行压缩,有效减小图片大小、占用内存空间等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | /// <summary> /// 生成缩略图 /// </summary> /// <param name="sourceFile">原始图片文件</param> /// <param name="quality">质量压缩比:0-100,越大质量越好</param> /// <param name="multiple">压缩倍数</param> /// <param name="outputFile">输出文件名</param> /// <returns>成功返回true,失败返回false</returns> /// 调用格式:GetThumImage("", 85L, 3, ""); public static bool GetThumImage( string sourceFile, long quality, int multiple, string outputFile) { try { // 获取图片信息 Bitmap sourceImage = new Bitmap(sourceFile); ImageCodecInfo myImageCodecInfo = GetEncoderInfo(sourceImage.RawFormat); if ( null == myImageCodecInfo) { return false ; } // 压缩质量 System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality; EncoderParameters myEncoderParameters = new EncoderParameters(1); EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, quality); myEncoderParameters.Param[0] = myEncoderParameter; // 按比例压缩 float _xWidth = sourceImage.Width; float _yWidth = sourceImage.Height; Bitmap newImage = new Bitmap(( int )(_xWidth / multiple), ( int )(_yWidth / multiple)); Graphics g = Graphics.FromImage(newImage); /// 可按需配置属性 //g.CompositingQuality = CompositingQuality.HighQuality; //g.CompositingMode = CompositingMode.SourceCopy; //g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage(sourceImage, 0, 0, (_xWidth / multiple), (_yWidth / multiple)); g.Dispose(); // 保存图片 newImage.Save(outputFile, myImageCodecInfo, myEncoderParameters); return true ; } catch { return false ; } } /// <summary> /// 获取图片编码信息 /// </summary> private static ImageCodecInfo GetEncoderInfo(ImageFormat format) { ImageCodecInfo[] encoders; encoders = ImageCodecInfo.GetImageEncoders(); for ( int j = 0; j < encoders.Length; ++j){ if (encoders[j].FormatID == format.Guid) { return encoders[j]; } } return null ; } |
提供一个在工作中压缩图片的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public string CompressImageString(string str) { string convertedImageString = string.Empty; byte[] b = Convert.FromBase64String(str); MemoryStream ms = new MemoryStream(b); Bitmap bitMap = new Bitmap(ms); bitMap = CompressImage(bitMap); byte[] byteArray = null; using (MemoryStream stream = new MemoryStream()) { bitMap.Save(stream, System.Drawing.Imaging.ImageFormat.Png); byteArray = new byte[stream.Length]; stream.Seek(0, SeekOrigin.Begin); if (stream.Read(byteArray, 0, Convert.ToInt32(stream.Length)) <= 0){ return "" ; } } convertedImageString = Convert.ToBase64String(byteArray); return convertedImageString; } private Bitmap CompressImage(Bitmap bitmap, double compressRatio) { int width = ( int )(bitmap.Width * compressRatio); int height = ( int )(bitmap.Height * compressRatio); System.Drawing.Image.GetThumbnailImageAbort myCallback = new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback); System.Drawing.Image myThumbnail = bitmap.GetThumbnailImage(width, height, myCallback, IntPtr.Zero); Bitmap compressBitmap = new Bitmap(myThumbnail); return compressBitmap; } public bool ThumbnailCallback() { return false ; } |
Base64处理
最常见的用于传输8Bit字节码的编码方式之一,可以将任意一组字节转换为较长的常见文本字符序列,从而可以合法地作为首部字段值
- Base64:一种基于64个可打印字符以及用作后缀的等号来表示二进制数据的方法
- Base64编码:从二进制到字符的过程,可用于在HTTP环境下传递较长的标识信息
- 编码规则:每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1/3个字符变成4个字符,每76个字符加一个换行符,最后的结束符也要处理
其中,64个可打印字符包括:大小写字母、数字、 + 和 / 。相关特点:
- 把含有不可见字符串的信息用可见字符串表示出来、降低出错率,但具有不可读性、需解码
- 二进制序列的长度必须是24的倍数(6和8的最小公倍数)
- 等号一定用作后缀,且数目一定是0个、1个或2个
- 因为将3个字节转化成4个字节,因此编码后的文本,会比原文本大出三分之一左右
1 | [ 'A' , 'B' , 'C' , 'D' , ... 'a' , 'b' , 'c' , 'd' , ... '0' , '1' , '2' , ... '+' , '/' ] |
注意,标准的Base64编码存在 '+'和 '/',针对URL,通过"url safe"的base64编码,将 '+'和 '/' 分别变成 '-'和 '_',同时会删除结果最后的 '='
1 2 3 4 | >>> base64.b64encode( 'i\xb7\x1d\xfb\xef\xff' ) 'abcd++//' >>> base64.urlsafe_b64encode( 'i\xb7\x1d\xfb\xef\xff' ) 'abcd--__' |
对于二进制序列长度必须是24倍数的解释:正常情况下,只要长度是6的倍数即可。但是,当连接两段Base64编码过的字符串后再解码,这个时候就需要6和8的公倍数,即长度必须是24的倍数。
现在浏览器已有内置的自动生成base64的方法 atob()
和 btoa()
1 2 3 4 | // 编码 window.btoa() // 解码 window.atob() |
具体参见:window.btoa()
;window.atob()
;
注意,待编码字符串若包含中文,直接编码会出现问题,应采用如下方式
1 2 3 4 5 6 7 8 9 10 | function utf8_to_b64( str ) { return window.btoa( unescape( encodeURIComponent( str ))); } function b64_to_utf8( str ) { return decodeURIComponent( escape( window.atob( str ))); } |
计算图片大小
1 2 3 4 5 6 7 8 9 | //base64串格式图片 public static double CalcuPicSize( string picstr) { double p_len = picstr.Length, fix_len = 0, p_ix = picstr.IndexOf( "=" ); if (p_ix > -1) { fix_len = p_len - p_ix; } double p_size = p_len - (p_len / 8) * 2 - fix_len; return p_size; } |
一种计算大小方法:base64图片计算大小,供参考
参考:
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· 程序员常用高效实用工具推荐,办公效率提升利器!
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)