Net core中使用System.Drawing对上传的图片流进行压缩

打印 上一主题 下一主题

主题 530|帖子 530|积分 1605

由于net core 中默认没有System.Drawing,可以通过nuget下载一个来代替System.Drawing.Common
 
直接压缩图片
  1. /// <summary>
  2. /// 图片压缩
  3. /// </summary>
  4. /// <param name="sFile">原图片位置</param>
  5. /// <param name="dFile">压缩后图片位置</param>
  6. /// <param name="dHeight">图片压缩后的高度</param>
  7. /// <param name="dWidth">图片压缩后的宽度</param>
  8. /// <param name="flag">图片压缩比0-100,数值越小压缩比越高,失真越多</param>
  9. /// <returns></returns>
  10. public static bool GetPicThumbnailTest(string sFile, string dFile, int dHeight, int dWidth, int flag)
  11. {
  12.     System.Drawing.Image iSource = System.Drawing.Image.FromFile(sFile);
  13.     //如果为参数为0就保持原图片的高宽嘛(不然想保持原图外面还要去读取一次)
  14.     if (dHeight == 0)
  15.     {
  16.         dHeight = iSource.Height;
  17.     }
  18.     if (dWidth == 0)
  19.     {
  20.         dWidth = iSource.Width;
  21.     }
  22.     ImageFormat tFormat = iSource.RawFormat;
  23.     int sW = 0, sH = 0;
  24.     //按比例缩放
  25.     Size tem_size = new Size(iSource.Width, iSource.Height);
  26.     if (tem_size.Width > dHeight || tem_size.Width > dWidth)
  27.     {
  28.         if ((tem_size.Width * dHeight) > (tem_size.Width * dWidth))
  29.         {
  30.             sW = dWidth;
  31.             sH = (dWidth * tem_size.Height) / tem_size.Width;
  32.         }
  33.         else
  34.         {
  35.             sH = dHeight;
  36.             sW = (tem_size.Width * dHeight) / tem_size.Height;
  37.         }
  38.     }
  39.     else
  40.     {
  41.         sW = tem_size.Width;
  42.         sH = tem_size.Height;
  43.     }
  44.     Bitmap ob = new Bitmap(dWidth, dHeight);
  45.     Graphics g = Graphics.FromImage(ob);
  46.     g.Clear(Color.WhiteSmoke);
  47.     g.CompositingQuality = CompositingQuality.HighQuality;
  48.     g.SmoothingMode = SmoothingMode.HighQuality;
  49.     g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  50.     g.DrawImage(iSource, new Rectangle((dWidth - sW) / 2, (dHeight - sH) / 2, sW, sH), 0, 0, iSource.Width, iSource.Height, GraphicsUnit.Pixel);
  51.     g.Dispose();
  52.     //以下代码为保存图片时,设置压缩质量  
  53.     EncoderParameters ep = new EncoderParameters();
  54.     long[] qy = new long[1];
  55.     qy[0] = flag;//设置压缩的比例1-100  
  56.     EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy);
  57.     ep.Param[0] = eParam;
  58.     try
  59.     {
  60.         ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders();
  61.         ImageCodecInfo jpegICIinfo = null;
  62.         for (int x = 0; x < arrayICI.Length; x++)
  63.         {
  64.             if (arrayICI[x].FormatDescription.Equals("JPEG"))
  65.             {
  66.                 jpegICIinfo = arrayICI[x];
  67.                 break;
  68.             }
  69.         }
  70.         if (jpegICIinfo != null)
  71.         {
  72.             ob.Save(dFile, jpegICIinfo, ep);//dFile是压缩后的新路径  
  73.         }
  74.         else
  75.         {
  76.             ob.Save(dFile, tFormat);
  77.         }
  78.         return true;
  79.     }
  80.     catch
  81.     {
  82.         return false;
  83.     }
  84.     finally
  85.     {
  86.         iSource.Dispose();
  87.         ob.Dispose();
  88.     }
  89. }
复制代码
  
通过文件流压缩图片

有些时候我们不想先把图片保存后,然后在去读取压缩,我们想通过文件流就直接对图片进行压缩了,比如我们要把图片上传到七牛云
先把流进行压缩在上传到七牛云就比较科学了
1:首先我们需要通过图片上传的流来获取图片
  1. foreach (IFormFile file in files)//获取多个文件列表集合
  2.             {
  3.                 if (file.Length > 0)
  4.                 {
  5.                     //获取图片上传的流
  6.                     Stream stream = file.OpenReadStream();
  7.                     //直接从流里边变成图片
  8.                     System.Drawing.Image iSource = System.Drawing.Image.FromStream(stream);
  9.                 }
  10.             }
复制代码
  
2:通过图片压缩算法把图片进行压缩
这里有一个参数是输入流,后面还有一个是压缩后的输出流
  1.         /// <summary>
  2.         /// 上传图片文件
  3.         /// </summary>
  4.         /// <returns></returns>
  5.         [HttpPost]
  6.         public async Task<IActionResult> UploadImageFile_WeChat()
  7.         {
  8.             var file = IHttpContextAccessor.HttpContext.Request.Form.Files;
  9.             if (file == null || file.Count == 0)
  10.             {
  11.                 return Fail("未上传有效文件");
  12.             }
  13.             var result = new List<dynamic>();
  14.             foreach (var item in file)
  15.             {
  16.                 var ExtensionName = Path.GetExtension(item.FileName).ToLower();
  17.                 var RemotePath = getRemotePath(ExtensionName);
  18.                 if (string.IsNullOrEmpty(RemotePath) || !"image".Equals(RemotePath))
  19.                 {
  20.                     return Fail("不支持此类型文件的上传");
  21.                 }
  22.                 string remotePath = PathFormatter.Format(item.FileName + "." + ExtensionName, "/upload/" + RemotePath + "/image" + "/{yyyy}{mm}/{dd}{time}{rand:6}");
  23.                 string savePath = AppDomain.CurrentDomain.BaseDirectory + "/wwwroot/" + remotePath;
  24.                 MemoryStream memoryStream = new MemoryStream();
  25.                 //ob.Save(memoryStream, jpegICIinfo, ep);//这里的ob就是压缩后的Bitmap对象
  26.                 var k = GetPicThumbnail(item.OpenReadStream(), 0, 0, 70, memoryStream);
  27.                 System.Drawing.Image imgSource = System.Drawing.Image.FromStream(memoryStream);
  28.                 imgSource.Save(savePath);
  29.                 if (k)
  30.                 {
  31.                     result.Add(new { url = Config.FileConfig.fileUrl + remotePath, remoteUrl = remotePath, name = item.FileName });
  32.                 }
  33.             }
  34.             return Success("上传成功", result);
  35.         }
  36.         private bool GetPicThumbnail(Stream stream, int dHeight, int dWidth, int flag, Stream outstream)
  37.         {
  38.             //可以直接从流里边得到图片,这样就可以不先存储一份了
  39.             System.Drawing.Image iSource = System.Drawing.Image.FromStream(stream);
  40.             //如果为参数为0就保持原图片
  41.             if (dHeight == 0)
  42.             {
  43.                 dHeight = iSource.Height;
  44.             }
  45.             if (dWidth == 0)
  46.             {
  47.                 dWidth = iSource.Width;
  48.             }
  49.             ImageFormat tFormat = iSource.RawFormat;
  50.             int sW = 0, sH = 0;
  51.             //按比例缩放
  52.             Size tem_size = new Size(iSource.Width, iSource.Height);
  53.             if (tem_size.Width > dHeight || tem_size.Width > dWidth)
  54.             {
  55.                 if ((tem_size.Width * dHeight) > (tem_size.Width * dWidth))
  56.                 {
  57.                     sW = dWidth;
  58.                     sH = (dWidth * tem_size.Height) / tem_size.Width;
  59.                 }
  60.                 else
  61.                 {
  62.                     sH = dHeight;
  63.                     sW = (tem_size.Width * dHeight) / tem_size.Height;
  64.                 }
  65.             }
  66.             else
  67.             {
  68.                 sW = tem_size.Width;
  69.                 sH = tem_size.Height;
  70.             }
  71.             Bitmap ob = new Bitmap(dWidth, dHeight);
  72.             Graphics g = Graphics.FromImage(ob);
  73.             g.Clear(Color.WhiteSmoke);
  74.             g.CompositingQuality = CompositingQuality.HighQuality;
  75.             g.SmoothingMode = SmoothingMode.HighQuality;
  76.             g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  77.             g.DrawImage(iSource, new Rectangle((dWidth - sW) / 2, (dHeight - sH) / 2, sW, sH), 0, 0, iSource.Width, iSource.Height, GraphicsUnit.Pixel);
  78.             g.Dispose();
  79.             //以下代码为保存图片时,设置压缩质量  
  80.             EncoderParameters ep = new EncoderParameters();
  81.             long[] qy = new long[1];
  82.             qy[0] = flag;//设置压缩的比例1-100  
  83.             EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy);
  84.             ep.Param[0] = eParam;
  85.             try
  86.             {
  87.                 ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders();
  88.                 ImageCodecInfo jpegICIinfo = null;
  89.                 for (int x = 0; x < arrayICI.Length; x++)
  90.                 {
  91.                     if (arrayICI[x].FormatDescription.Equals("JPEG"))
  92.                     {
  93.                         jpegICIinfo = arrayICI[x];
  94.                         break;
  95.                     }
  96.                 }
  97.                 if (jpegICIinfo != null)
  98.                 {
  99.                     //可以存储在流里边;
  100.                     ob.Save(outstream, jpegICIinfo, ep);
  101.                 }
  102.                 else
  103.                 {
  104.                     ob.Save(outstream, tFormat);
  105.                 }
  106.                 return true;
  107.             }
  108.             catch
  109.             {
  110.                 return false;
  111.             }
  112.             finally
  113.             {
  114.                 iSource.Dispose();
  115.                 ob.Dispose();
  116.             }
  117.         }
复制代码
  3:把压缩后的图片转化成流,很简单用一个内存流来中转一下就好了
  1. MemoryStream memoryStream = new MemoryStream();
  2.   ob.Save(memoryStream, jpegICIinfo, ep);//这里的ob就是压缩后的Bitmap对象
复制代码
     为了验证一下转化是否正确,我们可以把流在转化成图片然后在图片进行存储
  1. System.Drawing.Image imgSource = System.Drawing.Image.FromStream(memoryStream);
  2. imgSource.Save("url");
复制代码
  如果能够成功压缩并成功保存就说明这些步骤都成功了!
这里说一下图片传输的思路
图片文件这种本身是无法进行传输的,就像跨语言的对象也是无法进行传输。但是我们可以事先约定一种标准,
让双方都可以认识都可以解析的一种标准,比如base64,比如对象的json序列化,比如光纤信号的光波表示,其实原理都是一样。
 
上传到七牛云前压缩图片

 
通过上面的方法可以得到一个输出流,我们可以通过它进行图片的保存,但是如果直接把这个输出流传递到七牛云的方法中去,图片是不能被上传成功的,存储大小会是0kb,说明我们这个流七牛云的接口识别不到,也就是约定的内容不一样,我们要改造成七牛云能够被识别的状态
 
换一个方法尝试,直接用流不行,就从流里边读出来字节数组试试
  1. //实例化一个内存流,存放压缩后的图片
  2.     MemoryStream ysstream = new MemoryStream();
  3.     bool issuc = ImageTool.GetPicThumbnail(stream, 300, 300, 80, ysstream);
  4.     if (issuc)
  5.     {
  6.         //通过流上传图片到七牛云
  7.         //HttpResult result = um.UploadStream(stream, saveKey, uploadToken);
  8.         //从内存流里边读出来字节数组上传到七牛云
  9.         HttpResult result = um.UploadData(ysstream.ToArray(), saveKey, uploadToken);
  10.         if (result.Code == 200)
  11.         {
  12.             return Json(result.Text);
  13.         }
  14.         else
  15.         {
  16.             throw new Exception(result.RefText);//上传失败错误信息
  17.         }
  18.     }
  19.     else
  20.     {
  21.         throw new Exception("图片压缩失败");//上传失败错误信息
  22.     }
复制代码
  成功了
换回流试试呢,不应该啊。传递流进去他里边也应该是读取的直接哇,本质上都一样哇
还是不行,看来得看一下他这个源码了,看一下他拿到这个流过后是怎么去用的,就能针对性解决问题了
 
部署问题
在Windows环境下直接运行是没问题的,但是发布到Linux上就会报错

 
在Linux中安装
开始安装libgdiplus,执行【docker ps -a 】查看所有容器

 
 
【docker start 容器ID】 将容器运行起来

 
【docker exec -it e90f2b9d448d /bin/bash】进入该容器bash界面

 
 
执行【apt-get update】

 
【apt-get install -y libgdiplus】安装libgdiplus类库

 
 
【 ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll】创建链接文件
 
【eixt】退出docker bash到宿主机的bash,执行 【docker restart 容器ID】,此时接口已经能正确访问了

 

 
上面的方法有个弊端,假如容器被误删,又要重新给容器安装libgdiplus库。
我们可以把修改好的容器制作成镜像,执行【docker commit e90f2b9d448d skyapi_libgdiplus】,然后执行【docker images】,
可以看到名字叫skyapi_libgdiplus的Docker镜像已经制作好了。今后只需要在 docker run -t 参数后面指定 skyapi_libgdiplus镜像即可。
当前还可以将镜像保存到docker hub,本地硬盘都可以。

 
喜闻乐见的是,.NET 6发布了,但是避免不了新框架带来各种问题。在以往的跨平台应用中,往往采用System.Drawing.Common这个库作为图形编辑组件。
在.NET 6之前,在Linux操作系统中需要用到这个库时,只需要安装libgdiplus和libc6-dev这两个依赖即可。但是在.NET 6中,System.Drawing.Common被归为Windows特定的库,编译时产生“'Image.xxx()' is only supported on: 'windows'.”这样的警告。这不是最重要的,严重的是,在Linux中调用时,会产生“The type initializer for 'Gdip' threw an exception.”这样的异常。
产生原因
在设计上System.Drawing.Common 是 Windows 技术的精简包装器,因此其跨平台实现欠佳。
具微软文档中描述,在旧的行为上,libgdiplus 是本机端 System.Drawing.Common 跨平台实现的主要提供程序。 libgdiplus 实际上是对 System.Drawing.Common 所依赖的 Windows 部分的重新实现。 该实现使 libgdiplus 成为一个重要的组件。 它大约有 30,000 行 C 代码,大部分未经测试,而且缺少很多功能。 libgdiplus 还具有许多用于图像处理和文本呈现的外部依赖项,例如 cairo、pango 和其他本机库。 这些依赖项使得维护和交付组件更具挑战性。 自从包含 Mono 跨平台实现以来,我们已将许多从未得到修复的问题重定向到 libgdiplus。 相比之下,我们采用的其他外部依赖项,例如 icu 或 openssl,都是高质量的库。 使 libgdiplus 的功能集和质量与 .NET 堆栈的其余部分相媲美是不可行的。
在这之后,System.Drawing.Common 将仅在 Windows 窗体和 GDI+ 项目中使用。
解决方案
1、项目不会在Linux平台运行,仅在Windows中运行
可以忽略这个警告。
2、通过将 runtimeconfig.json 文件中的 System.Drawing.EnableUnixSupport 运行时配置开关设置为 true 来启用对非 Windows 平台的支持。
  1. {
  2.    "runtimeOptions": {
  3.       "configProperties": {
  4.          "System.Drawing.EnableUnixSupport": true
  5.       }
  6.    }
  7. }
复制代码
3、换用其它支持跨平台的图像处理库
如:
需要注意的是,这些库并不与System.Drawing.Common的API兼容,所以更换相应的库之后需要重新编写相关代码。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大连全瓷种植牙齿制作中心

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表