.NET Core 图片操作在 Linux/Docker 下的坑
一.前言
.NET Core 目前更新到2.2了,但是直到现在在 .NET Core 本身依然不包括和图片有关的 Image、Bitmap 等类型。对于图片的操作在我们开发中很常见,比如:生成验证码、二维码等等。在 .NET Core 的早期版本中,有 .NET 社区开发者实现了一些 System.Drawing 的 Image等类型实现的组件,比如 CoreCompat.System.Drawing、ZKWeb.System.Drawing等。后来微软官方提供了一个组件 System.Drawing.Common实现了 System.Drawing 的常用类型,以 Nuget 包的方式发布的。今天就围绕它来讲一讲这里面的坑。
在 .NET Core 中可以通过安装
System.Drawing.Common来使用 Image、Bitmap 等类型。
二.寻坑
本文将以一个 ASP.NET Core 项目使用 QRCoder 组件来生成一个二维码作为示例。
1.新建一个 ASP.NET Core 项目
2.安装 QRCoder
dotnet add package QRCoder
QRCoder是一个非常强大的生成二维码的组件,它使用了 System.Drawing.Common ,所以安装它用来做测试。
3.打开 ValuesController,添加如下代码:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpGet]
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.Length];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(data, 0, Convert.ToInt32(stream.Length));
return data;
}
}
上面的代码生成了一个二维码,通过API返回,文件名为 hello-dotnetcore.png
4.运行
(1)Windows
在 Windows 环境下我们直接运行,打开浏览器访问 http://localhost:5000/api/values

查看该图片:

一切正常
(2)Linux 或者 Docker(Linux)
Docker(Linux)指:以Linux系统为基础的镜像
我们将代码原封不动的拷贝到 Linux 上运行

使用curl访问
curl http://localhost:5000/api/values
查看日志输出可以见到报错了

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
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.
该异常的意思是: 找不到DLL libgdiplus,如何解决?请看下一小节。
三.埋坑
System.Drawing.Common 组件提供对GDI+图形功能的访问。它是依赖于GDI+的,那么在Linux上它如何使用GDI+,因为Linux上是没有GDI+的。Mono 团队使用C语言实现了GDI+接口,提供对非Windows系统的GDI+接口访问能力(个人认为是模拟GDI+,与系统图像接口对接),这个就是 libgdiplus。进而可以推测 System.Drawing.Common 这个组件实现时,对于非Windows系统肯定依赖了 ligdiplus 这个组件。如果我们当前系统不存在这个组件,那么自然会报错,找不到它,安装它即可解决。
libgdiplus github: https://github.com/mono/libgdiplus
1.CentOS
#一键命令
sudo curl https://raw.githubusercontent.com/stulzq/awesome-dotnetcore-image/master/install/centos7.sh|sh
或者
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
2.Ubuntu
#一键命令
sudo curl https://raw.githubusercontent.com/stulzq/awesome-dotnetcore-image/master/install/ubuntu.sh|sh
或者
apt-get update
apt-get install libgdiplus -y
ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll
3.Docker
Dockerfile 加入 RUN 命令,以官方 asp.net core runtime 镜像,以 asp.net core 2.2 作为示例:
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", "<你的入口程序集>"]
apt-get update 这一步是必不可少的,不然会报找不到 libgdiplus。但是官方镜像里面使用的软件包源又是国外的地址,所以造成我们使用国内网络非常慢,进而造成整体构建过程非常慢。下面有两个解决方案:
(1)直接使用打包好的Docker镜像
该镜像是基于微软官方镜像打包的,只安装了 libgdiplus,不添加任何添加剂。
将 Dockerfile 中的 FROM microsoft/dotnet:2.2.0-aspnetcore-runtime 换为 FROM stulzq/dotnet:2.2.0-aspnetcore-runtime-with-image
示例:
FROM stulzq/dotnet:2.2.0-aspnetcore-runtime-with-image
WORKDIR /app
COPY . .
EXPOSE 80
ENTRYPOINT ["dotnet", "<你的入口程序集>"]
(2)更换软件包源为国内源
此方法请看我以前写的文章:Docker实用技巧之更改软件包源提升构建速度
4.其他Linux发行版
首先查询下是否有编译好的 libgdiplus,如果没有可以到官方github查看教程,使用源码编译。
四.其他
这里要说明一下在 .NET Core 下,并非所有与图片操作有关的都需要安装 libgdiplus,只有你使用的组件依赖于 它提供的GDI+能力(依赖于它)才有必要装它。就比如你要是用 Image、Bitmap 类型,你就得安装 System.Drawing.Common ;或者你用的组件依赖了 System.Drawing.Common,比如 QRCoder。
有一些可以用于 .NET Core 的图片处理组件,自身没有依赖于 System.Drawing.Common,也没有依赖于 GDI+,使用它们是无需注意libgdiplus 这个问题的,比如 ImageSharp ,它使用纯C#实现了一些图片底层操作。
SkiaSharp 同样是可以进行图片操作的组件,在Linux上需要安装libSkiaSharp,SkiaSharp是由mono项目组提供的。我没有深入研究这个库,有兴趣的同学可以研究一下。
命令失效问题
有些同学可能遇到使用了命令无效的问题,据我猜测可能是包源或者是你本身环境导致安装失败,目前给出的解决办法有两个,一个是clone github源码编译安装,一个是下载离线包安装:https://pkgs.org/download/libgdiplus
五.结束
本文所诉问题,其实是个老问题了,网上也都有解决方案,本文是搁置很久(一直处于未编辑完状态)才发布的,这里就算做个总结吧。
本文所用测试代码、shell命令、以及 Dockerfile 都在github: https://github.com/stulzq/dotnetcore-image 如果觉得有用欢迎 Star 
.NET Core 图片操作在 Linux/Docker 下的坑的更多相关文章
- 关于.net Core项目发布在Linux上的填坑
本文主要记录.net Core项目发布在Linux服务器上面所遇到的问题,防止遗忘是 1.在发布文件中执行 dotnet xxxxxx.dll的时候提示如下错误: An assembly specif ...
- .net Core 中DateTime在Linux Docker中与Windows时间不一致
最近写了一个.net core项目,部署到CentOS并在docker上运行的时候,发现DateTime.Now获取的时间与Windows不一致(定时执行的任务,晚了8个小时),在Windows中可以 ...
- .NetCore&Linux&Docker&Portainer踩坑历险记
最近有一个云服务器和数据库的迁移任务,踩坑爬坑无数次,觉得必须要记录一下.大家瓜子花生准备好,听我慢慢讲故事#手动笑哭#. 故事背景 公司是做电商业务的,在天猫有几家旗舰店数据量也很大.阿里有一个称为 ...
- .NET Core跨平台:.NET Core项目部署到linux(Centos7)
1.开篇说明 a 上篇博客简单的说明了一下 使用.NET Core开发的一个总结,地址是:(http://www.cnblogs.com/hanyinglong/p/6442148.html),那么这 ...
- .Net Core 自动化部署:使用jenkins部署到linux docker容器运行
上次我们说到.Net Core 自动化部署:使用docker版jenkins部署dotnetcore应用,这次我们使用jenkins发布我们的.NET Core站点到docker容器中运行,为后面的的 ...
- 本地Docker Jenkins构建dotnet core web应用到Linux服务器 Docker上
1.准备工作 环境 本地: Windows.Docker 代码仓库:Git 服务器:Linux.Docker 前提准备 创建个有dockerfile文件的dotnet core 3 web项目 新建一 ...
- 将 ASP.NET Core 1.0 应用作为 docker 镜像发布 (Linux版)
var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...
- Linux环境下发布.net core
一.安装Linux环境 1. 安装VM虚拟机和操作系统 VM虚拟工具安装的过程详见:http://blog.csdn.net/stpeace/article/details/78598333.直接按照 ...
- Linux 使用 docker 下搭建xunsearch 搜索引擎服务
Linux 使用 docker 下搭建 xunsearch 搜索引擎服务 安装 docker 环境(菜鸟教程有说明) 安装docker说明 下载并运行 xunsearch 的服务端:docker安装x ...
随机推荐
- 6.[leetcode] ZigZag Conversion
The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like ...
- 简单读!zookeeper单机模式的启动逻辑
zk用处如此之多,以至于每个地方都要你理解zk原理! 请按如下操作姿势打开: 1. 打开zk的git仓库地址:https://github.com/apache/zookeeper , 确认过眼神,它 ...
- 距离度量以及python实现(一)
1. 欧氏距离(Euclidean Distance) 欧氏距离是最易于理解的一种距离计算方法,源自欧氏空间中两点间的距离公式. (1)二维平面上两点a(x1,y1)与b(x2,y2)间 ...
- 死磕 java集合之ConcurrentHashMap源码分析(一)
开篇问题 (1)ConcurrentHashMap与HashMap的数据结构是否一样? (2)HashMap在多线程环境下何时会出现并发安全问题? (3)ConcurrentHashMap是怎么解决并 ...
- docker~yml里使用现有网络
回到目录 我们在进行docker swarm部署高可用集群时,在yml文件里可能要配置一些服务,而这些服务可能要使用一些公用的数据库,这些数据库可能已经运行在某个容器里,而这些容器有自己的网络,doc ...
- 图像检索(4):IF-IDF,RootSift,VLAD
TF-IDF RootSift VLAD TF-IDF TF-IDF是一种用于信息检索的常用加权技术,在文本检索中,用以评估词语对于一个文件数据库中的其中一份文件的重要程度.词语的重要性随着它在文件中 ...
- java 并发多线程显式锁概念简介 什么是显式锁 多线程下篇(一)
目前对于同步,仅仅介绍了一个关键字synchronized,可以用于保证线程同步的原子性.可见性.有序性 对于synchronized关键字,对于静态方法默认是以该类的class对象作为锁,对于实例方 ...
- vue-resource实现数据的绑定、添加、删除
vue-resource实现数据的绑定.添加.删除 <!DOCTYPE html> <html lang="en"> <head> <ti ...
- nginx系列8:反向代理和负载均衡原理
反向代理是nginx的一个非常重要的功能. 反向代理 nginx支持四层反向代理和七层反向代理,如下图. 负载均衡 负载均衡是实现服务高性能和高可用的重要手段,而nginx是实现负载均衡的重要工具.
- 基于geotools的(两个)SHP要素变化提取方法预研
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1. 背景 我们用遥感的手段进行卫星特征提取.多幅影像间的特征变化提取的 ...