.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專案組提供的。我沒有深入研究這個庫,有興趣的同學可以研究一下。
五.結束
本文所訴問題,其實是個老問題了,網上也都有解決方案,本文是擱置很久(一直處於未編輯完狀態)才釋出的,這裡就算做個總結吧。
本文所用測試程式碼、shell命令、以及 Dockerfile 都在github: https://github.com/stulzq/dotnetcore-image 如果覺得有用歡迎 Star