您现在的位置是:网站首页> 编程资料编程资料

Net core中使用System.Drawing对上传的图片流进行压缩(示例代码)_实用技巧_

2023-05-24 358人已围观

简介 Net core中使用System.Drawing对上传的图片流进行压缩(示例代码)_实用技巧_

由于net core 中默认没有System.Drawing,可以通过nuget下载一个来代替System.Drawing.Common

直接压缩图片

///  /// 图片压缩 ///  /// 原图片位置 /// 压缩后图片位置 /// 图片压缩后的高度 /// 图片压缩后的宽度 /// 图片压缩比0-100,数值越小压缩比越高,失真越多 ///  public static bool GetPicThumbnailTest(string sFile, string dFile, int dHeight, int dWidth, int flag) { System.Drawing.Image iSource = System.Drawing.Image.FromFile(sFile); //如果为参数为0就保持原图片的高宽嘛(不然想保持原图外面还要去读取一次) if (dHeight == 0) { dHeight = iSource.Height; } if (dWidth == 0) { dWidth = iSource.Width; } ImageFormat tFormat = iSource.RawFormat; int sW = 0, sH = 0; //按比例缩放 Size tem_size = new Size(iSource.Width, iSource.Height); if (tem_size.Width > dHeight || tem_size.Width > dWidth) { if ((tem_size.Width * dHeight) > (tem_size.Width * dWidth)) { sW = dWidth; sH = (dWidth * tem_size.Height) / tem_size.Width; } else { sH = dHeight; sW = (tem_size.Width * dHeight) / tem_size.Height; } } else { sW = tem_size.Width; sH = tem_size.Height; } Bitmap ob = new Bitmap(dWidth, dHeight); Graphics g = Graphics.FromImage(ob); g.Clear(Color.WhiteSmoke); g.CompositingQuality = CompositingQuality.HighQuality; g.SmoothingMode = SmoothingMode.HighQuality; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage(iSource, new Rectangle((dWidth - sW) / 2, (dHeight - sH) / 2, sW, sH), 0, 0, iSource.Width, iSource.Height, GraphicsUnit.Pixel); g.Dispose(); //以下代码为保存图片时,设置压缩质量 EncoderParameters ep = new EncoderParameters(); long[] qy = new long[1]; qy[0] = flag;//设置压缩的比例1-100 EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy); ep.Param[0] = eParam; try { ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders(); ImageCodecInfo jpegICIinfo = null; for (int x = 0; x < arrayICI.Length; x++) { if (arrayICI[x].FormatDescription.Equals("JPEG")) { jpegICIinfo = arrayICI[x]; break; } } if (jpegICIinfo != null) { ob.Save(dFile, jpegICIinfo, ep);//dFile是压缩后的新路径 } else { ob.Save(dFile, tFormat); } return true; } catch { return false; } finally { iSource.Dispose(); ob.Dispose(); } }

通过文件流压缩图片

有些时候我们不想先把图片保存后,然后在去读取压缩,我们想通过文件流就直接对图片进行压缩了,比如我们要把图片上传到七牛云

先把流进行压缩在上传到七牛云就比较科学了

1:首先我们需要通过图片上传的流来获取图片

foreach (IFormFile file in files)//获取多个文件列表集合 { if (file.Length > 0) { //获取图片上传的流 Stream stream = file.OpenReadStream(); //直接从流里边变成图片 System.Drawing.Image iSource = System.Drawing.Image.FromStream(stream); } }

2:通过图片压缩算法把图片进行压缩

这里有一个参数是输入流,后面还有一个是压缩后的输出流

///  /// 上传图片文件 ///  ///  [HttpPost] public async Task UploadImageFile_WeChat() { var file = IHttpContextAccessor.HttpContext.Request.Form.Files; if (file == null || file.Count == 0) { return Fail("未上传有效文件"); } var result = new List(); foreach (var item in file) { var ExtensionName = Path.GetExtension(item.FileName).ToLower(); var RemotePath = getRemotePath(ExtensionName); if (string.IsNullOrEmpty(RemotePath) || !"image".Equals(RemotePath)) { return Fail("不支持此类型文件的上传"); } string remotePath = PathFormatter.Format(item.FileName + "." + ExtensionName, "/upload/" + RemotePath + "/image" + "/{yyyy}{mm}/{dd}{time}{rand:6}"); string savePath = AppDomain.CurrentDomain.BaseDirectory + "/wwwroot/" + remotePath; MemoryStream memoryStream = new MemoryStream(); //ob.Save(memoryStream, jpegICIinfo, ep);//这里的ob就是压缩后的Bitmap对象 var k = GetPicThumbnail(item.OpenReadStream(), 0, 0, 70, memoryStream); System.Drawing.Image imgSource = System.Drawing.Image.FromStream(memoryStream); imgSource.Save(savePath); if (k) { result.Add(new { url = Config.FileConfig.fileUrl + remotePath, remoteUrl = remotePath, name = item.FileName }); } } return Success("上传成功", result); } private bool GetPicThumbnail(Stream stream, int dHeight, int dWidth, int flag, Stream outstream) { //可以直接从流里边得到图片,这样就可以不先存储一份了 System.Drawing.Image iSource = System.Drawing.Image.FromStream(stream); //如果为参数为0就保持原图片 if (dHeight == 0) { dHeight = iSource.Height; } if (dWidth == 0) { dWidth = iSource.Width; } ImageFormat tFormat = iSource.RawFormat; int sW = 0, sH = 0; //按比例缩放 Size tem_size = new Size(iSource.Width, iSource.Height); if (tem_size.Width > dHeight || tem_size.Width > dWidth) { if ((tem_size.Width * dHeight) > (tem_size.Width * dWidth)) { sW = dWidth; sH = (dWidth * tem_size.Height) / tem_size.Width; } else { sH = dHeight; sW = (tem_size.Width * dHeight) / tem_size.Height; } } else { sW = tem_size.Width; sH = tem_size.Height; } Bitmap ob = new Bitmap(dWidth, dHeight); Graphics g = Graphics.FromImage(ob); g.Clear(Color.WhiteSmoke); g.CompositingQuality = CompositingQuality.HighQuality; g.SmoothingMode = SmoothingMode.HighQuality; g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage(iSource, new Rectangle((dWidth - sW) / 2, (dHeight - sH) / 2, sW, sH), 0, 0, iSource.Width, iSource.Height, GraphicsUnit.Pixel); g.Dispose(); //以下代码为保存图片时,设置压缩质量 EncoderParameters ep = new EncoderParameters(); long[] qy = new long[1]; qy[0] = flag;//设置压缩的比例1-100 EncoderParameter eParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, qy); ep.Param[0] = eParam; try { ImageCodecInfo[] arrayICI = ImageCodecInfo.GetImageEncoders(); ImageCodecInfo jpegICIinfo = null; for (int x = 0; x < arrayICI.Length; x++) { if (arrayICI[x].FormatDescription.Equals("JPEG")) { jpegICIinfo = arrayICI[x]; break; } } if (jpegICIinfo != null) { //可以存储在流里边; ob.Save(outstream, jpegICIinfo, ep); } else { ob.Save(outstream, tFormat); } return true; } catch { return false; } finally { iSource.Dispose(); ob.Dispose(); } }

  3:把压缩后的图片转化成流,很简单用一个内存流来中转一下就好了

MemoryStream memoryStream = new MemoryStream(); ob.Save(memoryStream, jpegICIinfo, ep);//这里的ob就是压缩后的Bitmap对象

   为了验证一下转化是否正确,我们可以把流在转化成图片然后在图片进行存储

System.Drawing.Image imgSource = System.Drawing.Image.FromStream(memoryStream); imgSource.Save("url");

  如果能够成功压缩并成功保存就说明这些步骤都成功了!

这里说一下图片传输的思路:

图片文件这种本身是无法进行传输的,就像跨语言的对象也是无法进行传输。但是我们可以事先约定一种标准,

让双方都可以认识都可以解析的一种标准,比如base64,比如对象的json序列化,比如光纤信号的光波表示,其实原理都是一样。

上传到七牛云前压缩图片

通过上面的方法可以得到一个输出流,我们可以通过它进行图片的保存,但是如果直接把这个输出流传递到七牛云的方法中去,图片是不能被上传成功的,存储大小会是0kb,说明我们这个流七牛云的接口识别不到,也就是约定的内容不一样,我们要改造成七牛云能够被识别的状态

换一个方法尝试,直接用流不行,就从流里边读出来字节数组试试

//实例化一个内存流,存放压缩后的图片 MemoryStream ysstream = new MemoryStream(); bool issuc = ImageTool.GetPicThumbnail(stream, 300, 300, 80, ysstream); if (issuc) { //通过流上传图片到七牛云 //HttpResult result = um.UploadStream(stream, saveKey, uploadToken); //从内存流里边读出来字节数组上传到七牛云 HttpResult result = um.UploadData(ysstream.ToArray(), saveKey, uploadToken); if (result.Code == 200) { return Json(result.Text); } else { throw new Exception(result.RefText);//上传失败错误信息 } } else { throw new Exception("图片压缩失败");//上传失败错误信息 }

  成功了

换回流试试呢,不应该啊。传递流进去他里边也应该是读取的直接哇,本质上都一样哇

还是不行,看来得看一下他这个源码了,看一下他拿到这个流过后是怎么去用的,就能针对性解决问题了

部署问题

在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 fo

-六神源码网