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

目录
  • 直接压缩图片
  • 通过文件流压缩图片
  • 上传到七牛云前压缩图片
    • 部署问题
    • 在Linux中安装
  • 产生原因
  • 解决方案

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

直接压缩图片

/// <summary>
/// 图片压缩
/// </summary>
/// <param name="sFile">原图片位置</param>
/// <param name="dFile">压缩后图片位置</param>
/// <param name="dHeight">图片压缩后的高度</param>
/// <param name="dWidth">图片压缩后的宽度</param>
/// <param name="flag">图片压缩比0-100,数值越小压缩比越高,失真越多</param>
/// <returns></returns>
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:通过图片压缩算法把图片进行压缩

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

/// <summary>
/// 上传图片文件
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> UploadImageFile_WeChat()
{
    var file = IHttpContextAccessor.HttpContext.Request.Form.Files;
    if (file == null || file.Count == 0)
    {
        return Fail("未上传有效文件");
    }
    var result = new List<dynamic>();
    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 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 平台的支持。

{
   "runtimeOptions": {
      "configProperties": {
         "System.Drawing.EnableUnixSupport": true
      }
   }
}

3、换用其它支持跨平台的图像处理库

如:

需要注意的是,这些库并不与System.Drawing.Common的API兼容,所以更换相应的库之后需要重新编写相关代码。

到此这篇关于Net core中使用System.Drawing对上传的图片流进行压缩的文章就介绍到这了,更多相关Net core图片压缩内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • .Net Core HttpClient处理响应压缩详细

    目录 一.使用方式 二.源码探究 前言: 在ASP.NET Core服务端处理关于响应压缩的请求,服务端的主要工作就是根据Content-Encoding头信息判断采用哪种方式压缩并返回.之前在群里有人问道过,现在的网络带宽这么高了还有必要在服务端针对请求进行压缩吗?确实,如今分布式和负载均衡技术这么成熟,很多需要处理高并发大数据的场景都可以通过增加服务器节点来进行.但是,在资源受限的情况下,或者是还没必要为了某一个点去增加新的服务器节点的时候,我们还是要采用一些程序本身的常规处理手段来进行处理

  • ASP.NET Core Web资源打包与压缩技术介绍

    概述 在ASP.Net中可以使用打包与压缩这两种技术来提高Web应用程序页面加载的性能.通过减少从服务器请求的次数和减少资源文件的体积来提高加载性能. 打包是一地将多个文件(CSS,JavaScript等资源文件)合并或打包到单个文件.文件合并可减少了 Web 资源文件从服务器的所需请求数,这样也可提高页面载入的性能. 压缩是将各种不同的代码进行优化,以减少请求资源文件的体积.压缩的常见方法删除不必要的空格和注释,并将变量名缩减为一个字符. 例如下面JavaScript函数: AddAltToI

  • .Net Core 多文件打包压缩的实现代码

    最近项目需要实现多文件打包的功能,尝试了一些方法,最后发现使用  ICSharpCode.SharpZipLib 最符合项目的要求. 具体实现如下: 1.在 Nuget 中安装  ICSharpCode.SharpZipLib 2.将要打包的文件放到同个文件夹进行压缩: ①压缩文件夹 /// <summary> /// 压缩文件 /// </summary> /// <param name="fileName">压缩后获得的文件名</param

  • ASP.NET Core文件压缩常见使用误区(最佳实践)

    前言 在微软官方文档中,未明确指出文件压缩功能的使用误区. 本文将对 ASP.NET Core 文件响应压缩的常见使用误区做出说明. 误区1:未使用Brotil 压缩 几乎不需要任何额外的代价,Brotil 压缩算法可以帮助你的网站提升约 20% 静态资源加载性能. 同时启用 Gzip / Brotil 压缩 Gzip 有更好的 user-agent 兼容性,而 Brotli 有更好的性能. 所以我们通常需要在 ASP.NET Core 网站中同时启用这两种压缩. 如何区分 Gzip 压缩和 B

  • .netcore+vue 实现压缩文件下载功能

    一.前言 目前接触的项目中,给定的需求是将系统内所有用户的数据整理好,并保存到文件夹内,目的主要是防止用户在实施人员已配置好的基础上由于不熟悉系统,导致的误删或者误操作.减少实施人员的配置工作.我首先想到的就是将数据导入到Excel中,并以各个用户的名称命名文件夹做好分类. vue下实现Excel导入这个我们见的比较多了,当时我也确实实现了下载Excel的功能,但是后续发现保存的文件都在服务器上,那就有一个问题了,实施人员是通过页面点击的一键保存按钮,数据也确实保存了,但是却是在服务器上,如果想

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

    目录 直接压缩图片 通过文件流压缩图片 上传到七牛云前压缩图片 部署问题 在Linux中安装 产生原因 解决方案 由于net core 中默认没有System.Drawing,可以通过nuget下载一个来代替System.Drawing.Common 直接压缩图片 /// <summary> /// 图片压缩 /// </summary> /// <param name="sFile">原图片位置</param> /// <para

  • Linux/Docker 中使用 System.Drawing.Common 踩坑记录分享

    前言 在项目迁移到 .net core 上面后,我们可以使用 System.Drawing.Common 组件来操作 Image,Bitmap 类型,实现生成验证码.二维码,图片操作等功能.System.Drawing.Common 组件它是依赖于 GDI+ 的,然后在 Linux 上并没有 GDI+,面向谷歌编程之后发现,Mono 团队使用 C语言 实现了GDI+ 接口,提供对非Windows系统的 GDI+ 接口访问能力,这个应该就是libgdiplus.所以想让代码在 linux 上稳定运

  • React组件内事件传参实现tab切换的示例代码

    本文介绍了React组件内事件传参实现tab切换的示例代码,分享给大家,具体如下: 组件内默认onClick事件触发函数actionClick, 是不带参数的, 不带参数的写法: 如onClick= { actionItem } 带参数的写法, onClick = { this.activateButton.bind(this, 0) } 下面是一个向组件内函数传递参数的小例子 需求: 在页面的底部, 有四个按钮, 负责切换内容, 当按钮被点击时, 变为激活状态, 其余按钮恢复到未激活状态 分析

  • Java中利用Alibaba开源技术EasyExcel来操作Excel表的示例代码

    一.读Excel 1.Excel表格示例 2.对象示例 @Data public class DemoData { private String string; private Date date; private Double doubleData; } 3.监听器(重点部分) // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 public class DemoDataListener e

  • Android 中CheckBox多项选择当前的position信息提交的示例代码

    先给大家展示下效果图: 废话不多说了,下面通过示例代码给大家介绍checkbox 多项选择当前的position信息提交,具体代码如下所示: package com.dplustours.b2c.View.activity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import andro

  • Python中实现一行拆多行和多行并一行的示例代码

    粉丝提问 今天粉丝提了下面这样一个问题,其中一个是"一行拆多行",另外一个是"多行并一行",貌似群友用power query已经解决了.但是基于Python怎么做呢?接着往下看. 一行拆多行 上面这个问题我会提供两个思路,供大家选择,当然肯定是越简单得越好.每一种方法中都有一些好用的技巧,希望大家能够好好学习. 1)方法一 下方代码中有很多重要的知识点,需要我们下去好好学习一下,我这里只提供解体思路,关于每个知识点怎么用,希望大家下去自行研究学习. Pandas.m

  • layui框架实现文件上传及TP3.2.3(thinkPHP)对上传文件进行后台处理操作示例

    本文实例讲述了layui框架实现文件上传及TP3.2.3对上传文件进行后台处理操作.分享给大家供大家参考,具体如下: layui框架是1.0.9版本.. 首先html页面代码如下: <div class="layui-form-item" id="upload_file"> <div class="layui-input-block" style="width: 300px;"> <input t

  • Java实现拖拽文件上传dropzone.js的简单使用示例代码

    Java实习生一枚,前端知识薄弱,最近因为工作需要,做了一个拖拽文件上传的功能,发现dropzone.js挺不错的,特地做个笔记. dropzonejs 的官网是:http://www.dropzonejs.com/, 中文手册是:http://wxb.github.io/dropzonejs.com.zh-CN/ 自己写的拖拽文件至一个按钮上传的功能,前端及java代码如下: jsp页面: 1. 首先必须引入dropzone的js和css文件 <link rel="stylesheet&

  • Vue页面内公共的多类型附件图片上传区域并适用折叠面板(示例代码)

    在前端项目中,附件上传是很常用的功能,几乎所有的app相关项目中都会使用到,一般在选择使用某个前端UI框架时,可以查找其内封装好的图片上传组件,但某些情况下可能并不适用于自身的项目需求,本文中实现的附件上传区域支持超多类型附件分类型上传,并且可根据特定条件具体展示某些类型的附件上传,本文中是直接摘自具体的页面,后面会抽时间单独封装出来一个附件上传的组件. 一.Vue页面内附件展示区域代码 <div class="retuinfo"> <div class="

  • Django Admin 上传文件到七牛云的示例代码

    中文圈关于Django Admin 上传文件到七牛云的资料和函数库已经是2年前的了,国外的则都是关于AWS S3.Azure Storage一些国外的服务的.我根据Django的文档里提到的存储系统来实现上传文件到七牛云的简单功能. 在Django Admin的表单是根据数据模型生成的,其中文件上传由FileField和继承FileField的ImageField来决定的,文件上传到静态文件目录,数据库保存相对路径.实现上传文件到七牛云我们是根据FileField的storage参数来实现的.

随机推荐