.NET Core 图片操作在 Linux/Docker 下的坑
<h2 id="一前言">一.前言</h2><p>.NET Core 目前更新到2.2了,但是直到现在在 .NET Core 本身依然不包括和图片有关的 Image、Bitmap 等类型。对于图片的操作在我们开发中很常见,比如:生成验证码、二维码等等。在 .NET Core 的早期版本中,有 .NET 社区开发者实现了一些 System.Drawing 的 Image等类型实现的组件,比如 <code>CoreCompat.System.Drawing</code>、<code>ZKWeb.System.Drawing</code>等。后来微软官方提供了一个组件 <code>System.Drawing.Common</code>实现了 System.Drawing 的常用类型,以 Nuget 包的方式发布的。今天就围绕它来讲一讲这里面的坑。</p>
<blockquote>
<p>在 .NET Core 中可以通过安装 <code>System.Drawing.Common</code> 来使用 Image、Bitmap 等类型。</p>
</blockquote>
<h2 id="二寻坑">二.寻坑</h2>
<p>本文将以一个 ASP.NET Core 项目使用 <code>QRCoder</code> 组件来生成一个二维码作为示例。</p>
<h3 id="1新建一个-aspnet-core-项目">1.新建一个 ASP.NET Core 项目</h3>
<h3 id="2安装-qrcoder">2.安装 QRCoder</h3>
<pre><code class="language-shell">dotnet add package QRCoder
</code></pre>
<p>QRCoder是一个非常强大的生成二维码的组件,它使用了 <code>System.Drawing.Common</code> ,所以安装它用来做测试。</p>
<h3 id="3打开-valuescontroller添加如下代码">3.打开 <code>ValuesController</code>,添加如下代码:</h3>
<pre><code class="language-csharp">")]
public class ValuesController : ControllerBase
{
public FileResult Get()
{
QRCodeGenerator.ECCLevel eccLevel = QRCodeGenerator.ECCLevel.L;
using (QRCodeGenerator qrGenerator = new QRCodeGenerator())
{
using (QRCodeData qrCodeData = qrGenerator.CreateQrCode("Hello .NET Core", eccLevel))
{
using (QRCode qrCode = new QRCode(qrCodeData))
{
Bitmap bp = qrCode.GetGraphic(20, Color.Black, Color.White,true);
return File(Bitmap2Byte(bp), "image/png", "hello-dotnetcore.png");
}
}
}
}
public static byte[] Bitmap2Byte(Bitmap bitmap)
{
using (MemoryStream stream = new MemoryStream())
{
bitmap.Save(stream, ImageFormat.Jpeg);
byte[] data = new byte;
stream.Seek(0, SeekOrigin.Begin);
stream.Read(data, 0, Convert.ToInt32(stream.Length));
return data;
}
}
</code></pre>
<p>上面的代码生成了一个二维码,通过API返回,文件名为 hello-dotnetcore.png</p>
<h3 id="4运行">4.运行</h3>
<h4 id="1windows">(1)Windows</h4>
<p>在 Windows 环境下我们直接运行,打开浏览器访问 <code>http://localhost:5000/api/values</code></p>
<p><img src="https://img2018.cnblogs.com/blog/668104/201812/668104-20181225103608906-273316081.png" alt="1545645378722" loading="lazy"></p>
<p>查看该图片:</p>
<p><img src="https://img2018.cnblogs.com/blog/668104/201812/668104-20181225103608514-239493911.png" alt="1545645410915" loading="lazy"></p>
<p>一切正常</p>
<h4 id="2linux-或者-dockerlinux">(2)Linux 或者 Docker(Linux)</h4>
<blockquote>
<p>Docker(Linux)指:以Linux系统为基础的镜像</p>
</blockquote>
<p>我们将代码原封不动的拷贝到 Linux 上运行</p>
<p><img src="https://img2018.cnblogs.com/blog/668104/201812/668104-20181225103608185-373324444.png" alt="1545645647336" loading="lazy"></p>
<p>使用curl访问</p>
<pre><code class="language-shell">curl http://localhost:5000/api/values
</code></pre>
<p>查看日志输出可以见到报错了</p>
<p><img src="https://img2018.cnblogs.com/blog/668104/201812/668104-20181225103607822-1397855338.png" alt="1545645739357" loading="lazy"></p>
<pre><code>fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware
An unhandled exception has occurred while executing the request.
System.TypeInitializationException: The type initializer for 'Gdip' threw an exception. ---> System.DllNotFoundException: Unable to load DLL 'libgdiplus': The specified module could not be found.
</code></pre>
<p>该异常的意思是: 找不到DLL libgdiplus,如何解决?请看下一小节。</p>
<h2 id="三埋坑">三.埋坑</h2>
<p><code>System.Drawing.Common</code> 组件提供对GDI+图形功能的访问。它是依赖于GDI+的,那么在Linux上它如何使用GDI+,因为Linux上是没有GDI+的。Mono 团队使用C语言实现了GDI+接口,提供对非Windows系统的GDI+接口访问能力(个人认为是模拟GDI+,与系统图像接口对接),这个就是 <code>libgdiplus</code>。进而可以推测 <code>System.Drawing.Common</code> 这个组件实现时,对于非Windows系统肯定依赖了 <code>ligdiplus</code> 这个组件。如果我们当前系统不存在这个组件,那么自然会报错,找不到它,安装它即可解决。</p>
<blockquote>
<p>libgdiplus github: https://github.com/mono/libgdiplus</p>
</blockquote>
<h3 id="1centos">1.CentOS</h3>
<pre><code class="language-shell">#一键命令
sudo curl https://raw.githubusercontent.com/stulzq/awesome-dotnetcore-image/master/install/centos7.sh|sh
</code></pre>
<p>或者</p>
<pre><code class="language-shell">yum update
yum install libgdiplus-devel -y
ln -s /usr/lib64/libgdiplus.so /usr/lib/gdiplus.dll
ln -s /usr/lib64/libgdiplus.so /usr/lib64/gdiplus.dll
</code></pre>
<h3 id="2ubuntu">2.Ubuntu</h3>
<pre><code class="language-shell">#一键命令
sudo curl https://raw.githubusercontent.com/stulzq/awesome-dotnetcore-image/master/install/ubuntu.sh|sh
</code></pre>
<p>或者</p>
<pre><code class="language-shell">apt-get update
apt-get install libgdiplus -y
ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll
</code></pre>
<h3 id="3docker">3.Docker</h3>
<p>Dockerfile 加入 RUN 命令,以官方 asp.net core runtime 镜像,以 asp.net core 2.2 作为示例:</p>
<pre><code class="language-shell">FROM microsoft/dotnet:2.2.0-aspnetcore-runtime
WORKDIR /app
COPY . .
RUN apt-get update -y && apt-get install -y libgdiplus && apt-get clean && ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll
EXPOSE 80
ENTRYPOINT ["dotnet", "<你的入口程序集>"]
</code></pre>
<p>apt-get update 这一步是必不可少的,不然会报找不到 libgdiplus。但是官方镜像里面使用的软件包源又是国外的地址,所以造成我们使用国内网络非常慢,进而造成整体构建过程非常慢。下面有两个解决方案:</p>
<h4 id="1直接使用打包好的docker镜像">(1)直接使用打包好的Docker镜像</h4>
<p>该镜像是基于微软官方镜像打包的,只安装了 <code>libgdiplus</code>,不添加任何添加剂。</p>
<p>将 Dockerfile 中的 <code>FROM microsoft/dotnet:2.2.0-aspnetcore-runtime</code> 换为 <code>FROM stulzq/dotnet:2.2.0-aspnetcore-runtime-with-image</code></p>
<p>示例:</p>
<pre><code class="language-shell">FROM stulzq/dotnet:2.2.0-aspnetcore-runtime-with-image
WORKDIR /app
COPY . .
EXPOSE 80
ENTRYPOINT ["dotnet", "<你的入口程序集>"]
</code></pre>
<h4 id="2更换软件包源为国内源">(2)更换软件包源为国内源</h4>
<p>此方法请看我以前写的文章:Docker实用技巧之更改软件包源提升构建速度</p>
<h3 id="4其他linux发行版">4.其他Linux发行版</h3>
<p>首先查询下是否有编译好的 libgdiplus,如果没有可以到官方github查看教程,使用源码编译。</p>
<h2 id="四其他">四.其他</h2>
<p>这里要说明一下在 .NET Core 下,并非所有与图片操作有关的都需要安装 libgdiplus,只有你使用的组件依赖于 它提供的GDI+能力(依赖于它)才有必要装它。就比如你要是用 Image、Bitmap 类型,你就得安装 <code>System.Drawing.Common</code> ;或者你用的组件依赖了 <code>System.Drawing.Common</code>,比如 <code>QRCoder</code>。</p>
<p>有一些可以用于 .NET Core 的图片处理组件,自身没有依赖于 <code>System.Drawing.Common</code>,也没有依赖于 GDI+,使用它们是无需注意<code>libgdiplus</code> 这个问题的,比如 <code> ImageSharp</code> ,它使用纯C#实现了一些图片底层操作。</p>
<blockquote>
<p>SkiaSharp 同样是可以进行图片操作的组件,在Linux上需要安装libSkiaSharp,SkiaSharp是由mono项目组提供的。我没有深入研究这个库,有兴趣的同学可以研究一下。</p>
</blockquote>
<h4 id="命令失效问题">命令失效问题</h4>
<p>有些同学可能遇到使用了命令无效的问题,据我猜测可能是包源或者是你本身环境导致安装失败,目前给出的解决办法有两个,一个是clone github源码编译安装,一个是下载离线包安装:https://pkgs.org/download/libgdiplus</p>
<h2 id="五结束">五.结束</h2>
<p>本文所诉问题,其实是个老问题了,网上也都有解决方案,本文是搁置很久(一直处于未编辑完状态)才发布的,这里就算做个总结吧。</p>
<p>本文所用测试代码、shell命令、以及 Dockerfile 都在github: https://github.com/stulzq/dotnetcore-image 如果觉得有用欢迎 Star <img src="https://img2018.cnblogs.com/blog/668104/201812/668104-20181225103607324-1479791581.png" alt="img" loading="lazy"></p>
</div>
<div id="MySignature" role="contentinfo">
<blockquote>
<strong>目前学习.NET Core 最好的教程 .NET Core 官方教程 ASP.NET Core 官方教程</strong>
</blockquote><br><br>
来源:https://www.cnblogs.com/stulzq/p/10172550.html
頁:
[1]