Using System.Drawing inside Docker - c#

I have followed these steps:
Create a new ASP.NET Core MVC web application without Docker support
Installed the System.Drawing.Common Nuget packages
Add a file called TestImage.png
Access the file successfully using the following code:
byte[] imageLoaded = System.IO.File.ReadAllBytes("TestImage.png");
Image image;
using (MemoryStream mStream = new MemoryStream(imageLoaded))
{
image = Image.FromStream(mStream);
}
var image2 = new Bitmap(image);
image2.Save("TestImage1.png", ImageFormat.Png);
Expected behaviour up to now; here is the problematic bit:
Right click on the solution and select: Add Container Orchestration Support/Docker Compose/Linux (target OS)
Run the project (docker-compose is the startup project). I get an error:
DllNotFoundException: Unable to load shared library 'libdl' or one of its dependencies. In order to help diagnose loading problems, consider setting the LD_DEBUG environment variable: liblibdl: cannot open shared object file: No such file or directory
I have done a lot of Googling and found this: Unable to load DLL 'libdl' when using System.Drawing.Common NuGet package on AWS Lambda
Here is my Dockerfile:
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY WebApplication1/WebApplication1.csproj WebApplication1/
RUN dotnet restore WebApplication1/WebApplication1.csproj
COPY . .
WORKDIR /src/WebApplication1
RUN dotnet build WebApplication1.csproj -c Release -o /app
FROM build AS publish
RUN dotnet publish WebApplication1.csproj -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]
I have tried adding the following lines:
# install System.Drawing native dependencies
RUN apt-get update \
&& apt-get install -y --allow-unauthenticated \
libc6-dev \
libgdiplus \
libx11-dev \
&& rm -rf /var/lib/apt/lists/*
However, it has made no difference. How can I fix this? or Is there another way I can approach this without using System.Drawing.Common.
However, it has not worked. How can I fix this?
Alternatively, is there a way to code this avoiding system.drawing?

I have managed to make this to work.
This is how my dockerfile looks like:
(I'm using .net core 3.0 but it should work perfectly fine with 2.x versions)
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base
RUN apt-get update \
&& apt-get install -y --no-install-recommends libgdiplus libc6-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN cd /usr/lib && ln -s libgdiplus.so gdiplus.dll
WORKDIR /app
... the rest of your dockerfile ...
I hope this helps.

Related

System.drawing.common the type initializer for 'gdip' threw an exception

This is my code to add a picture to a worksheet. I get the picture as a byte from the database. .Net Core framework version is 2.2.104. This is an API project. In my locale, the code works well. I use the ClosedXML component 0.95.4 version as below.
[HttpPost("GetTowel")]
public IActionResult GetTowel()
{
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
string fileName = "Towel Quotation.xlsx";
try
{
using (var workbook = new XLWorkbook())
{
IXLWorksheet worksheet = workbook.Worksheets.Add("Towel Quotation");
byte[] bytes = _fileService.Get(159).FileInBytes;
System.IO.Stream x = new System.IO.MemoryStream(bytes);
//the exception is throwed at this line:
**var image = worksheet.AddPicture(x).MoveTo(worksheet.Cell("P1")).Scale(1.0);**
using (var stream = new MemoryStream())
{
workbook.SaveAs(stream);
var content = stream.ToArray();
return File(content, contentType, fileName);
}
}
}
catch (Exception ex)
{
return BadRequest(ErrorResultFormatter.PrepareErrorResult("",ex.Message));
}
}
My Kubernetes server information is below:
System.drawing.common the type initializer for 'gdip' threw an exception
*FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build WORKDIR /app
COPY *.csproj Nuget.Config ./ RUN dotnet restore /property:Configuration=Release
--configfile=Nuget.Config --no-cache --force
COPY . ./temp/ WORKDIR /app/temp RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS runtime ENV ASPNETCORE_URLS="http://+" ENV ASPNETCORE_Kestrel__Certificates__Default__Password="*****" WORKDIR /app COPY --from=build /app/temp/out ./ ENTRYPOINT ["dotnet", "blahblah.dll"]*
On the server-side, I get the exception as below: "system.drawing.common the type initializer for 'gdip' threw an exception"
I have searched many times on google. That way is suggested generally to add docker file:
RUN apt-get install libgdiplus
But this way also didn't solve my problem. Can anybody help me?
Thanks in advance.
I have a Dockerfile like this:
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
RUN apt-get update && apt-get install -y apt-utils libgdiplus libc6-dev
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ...
.
.
.
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "...dll"]
I am running apt-get update and install command on the second line. You may run your command like this. I hope it works for you too.
The most accepted answer did not work for me on ubuntu 20.04 and it looks like support for non-windows os's is removed for .NET 6:
System.Drawing.Common only supported on Windows
The suggested alternatives are:
To use these APIs for cross-platform apps, migrate to one of the
following libraries:
ImageSharp https://github.com/SixLabors/ImageSharp
SkiaSharp https://github.com/mono/SkiaSharp
Microsoft.Maui.Graphics https://github.com/dotnet/Microsoft.Maui.Graphics
Fix for ClosedXML 0.96.0, .NET 6, and Alpine Linux throwing the following exception when creating an Excel file:
The type initializer for 'Gdip' threw an exception
Add the libgdiplus package
Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0-alpine
RUN apk add libgdiplus --repository http://dl-.alpinelinux.org/alpine/edge/testing/
RUN apk add ttf-freefont libssl1.1
...
Enable System.Drawing support for non-Windows platforms: (reference):
In your project file (*.csproj), add:
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />
</ItemGroup
I had this problem when deploying on Linux server
try it :
sudo apt-get install -y libgdiplus
and restart server
sudo systemctl restart nginx
For me, the following fixed the issue (in local tests and also deployments using Ubuntu Agent in Azure):
Here is what I have in my Dockerfile:
FROM mcr.microsoft.com/dotnet/aspnet:6.0 as base
RUN apt-get update && apt-get install -y libgdiplus
WORKDIR /app
EXPOSE 80
Here is what I have added into every *.csproj file:
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />
</ItemGroup>
And I also had to install this NuGet package too: "System.Drawing.Common"
Try to install the libgdi NuGet package within your project. See: https://github.com/eugeneereno/libgdiplus-packaging
dotnet add package ereno.linux-x64.eugeneereno.System.Drawing
For Mac users see: https://github.com/eugeneereno/libgdiplus-packaging

Docker container - no symbols have been loaded for this document

I already have my Visual Studio Code attached to my running container. It's a .Net Core WepApi. This is the link for the post I created when struggling to make it work.
The problem, I am facing now, is that despite the fact my VS Code is attached to the container I can't debug any code yet. It keeps saying:
No symbols has been loaded for this document
Which is kind of odd because I can see the pdb files where generated and deploy to the container:
When I attached the Docker Container my VS Code looks like this:
This is the updated docker file I have:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build-env
WORKDIR /app
# Copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# Copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out
###########START NEW IMAGE###########################################
# Build runtime image
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
#install debugger for NET Core
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1
RUN apt update && \
apt install -y unzip procps && \
curl -sSL https://aka.ms/getvsdbgsh | /bin/sh /dev/stdin -v latest -l /vsdbg
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "BooksApi.dll"]
Has anyone have face a similar problem?

How to fix exception Unable to load DLL 'gdiplus.dll': The specified module could not be found

I am developing a service fabric mesh application in .Net Core 2.1, in which Can't use System.Drawing.Common in microsoft/dotnet:runtime (windows container)
Below is my docker file code
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY MS_Upload/MS_Upload.csproj MS_Upload/
RUN dotnet restore MS_Upload/MS_Upload.csproj
COPY . .
WORKDIR /src/MS_Upload
RUN dotnet build MS_Upload.csproj -c Release -o /app
FROM build AS publish
RUN dotnet publish MS_Upload.csproj -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "MS_Upload.dll"]
The error I'm getting:
Unable to load DLL 'gdiplus.dll': The specified module could not be found.
at System.Drawing.SafeNativeMethods.Gdip.GdipCreateBitmapFromStream(IStream stream, IntPtr& bitmap)
at System.Drawing.Bitmap..ctor(Stream stream, Boolean useIcm)
at System.Drawing.Bitmap..ctor(Stream stream)
at MS_Upload.UserImageMaster.UploadAllChunkImageLibrary(Int32 iPeppyPrintID, Int32 iSourceID, String sAlbumID, Int32 iPickUpOrderID, String sFileName, Int32 iTotalChunk)
Try using linux container instead as libgdiplus modules aren't installed by default on windows containers, most likely it will work without issues
install system.Drawings dependencies using the following commands (reference):
RUN apt-get update \
&& apt-get install -y --allow-unauthenticated \
libc6-dev \
libgdiplus \
libx11-dev \
&& rm -rf /var/lib/apt/lists/*
you can use chocolatey to install those dependencies on windows container if using windows image is necessary

How to run dotnet core app with Selenium in Docker

I have dotnet core 2.2 (aspnet core) app running in Docker container. I'm using the simplest possible Dockerfile you can find in any basic tutorial:
use microsoft/dotnet:2.2-sdk as base image
copy *.csproj
restore packages
build
publish to /app folder
use microsoft/dotnet:2.2.1-aspnetcore-runtime to run the app from /app folder
Now I'd like to grab some data from another website. It is a SPA, so I need to use a browser to render the page first - I decided to use Selenium with ChromeDriver because I'm already a little bit familiar with them.
I've added Selenium.WebDriver v3.141 and Selenium.WebDriver.ChromeDriver v73.0 to my project, set Selenium there. Locally on Windows it works fine. But when I run this via Docker I'm getting:
The file /app/chromedriver does not exist. The driver can be downloaded at http://chromedriver.storage.googleapis.com/index.html
So now I'm wondering how can I run Selenium + single instance Chrome (there is no need to set up Selenium Grid for my purpose) with dotnet core 2.2 in Docker.
I suppose I need to create custom Dockerfile which:
installs selenium, chrome and all their dependencies
installs dotnet
does the same as my current Dockerfile to build and run my app
But I'm not really sure how to do this. Especially how to "nest" Dockerfiles.
Should I do this composition in a single Dockerfile? Should I create Dockerfile for Selenium + ChromeDriver and use it as base image for next step?
So I recently had the same problem.
TL;DR; You have to install chrome into the docker image by putting the
commands in the Docker file.
FROM mcr.microsoft.com/dotnet/core/sdk:2.2-stretch
# Install Chrome
RUN apt-get update && apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
hicolor-icon-theme \
libcanberra-gtk* \
libgl1-mesa-dri \
libgl1-mesa-glx \
libpango1.0-0 \
libpulse0 \
libv4l-0 \
fonts-symbola \
--no-install-recommends \
&& curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list \
&& apt-get update && apt-get install -y \
google-chrome-stable \
--no-install-recommends \
&& apt-get purge --auto-remove -y curl \
&& rm -rf /var/lib/apt/lists/*
# Add your dotnet core project build stuff here
Easier solution - I pushed this as a docker image in my docker hub repo so you can use it as your base image. See this example of my dotnet core 2.2
FROM masteroleary/selenium-dotnetcore2.2-linux:v2 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM masteroleary/selenium-dotnetcore2.2-linux:v2 AS build WORKDIR /src
COPY ["MyProject.csproj", ""]
RUN dotnet restore "MyProject.csproj"
COPY . .
WORKDIR "/src/"
RUN dotnet build "MyProject.csproj" -c Prod -o /app
FROM build AS publish
RUN dotnet publish "MyProject.csproj" -c Prod -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "MyProject.dll"]
How did this happen?
Basically created a new project in visual studio for dotnet core 2.2 mvc with docker support.
Intentions are to run my dotnet core app in a linux container
Assumed that by installing nuget packages Selenium.Support, Selenium.WebDriver, Selenium.WebDriver.ChromeDriver anything I needed would be included in the docker container automatically since Selenium.WebDriver supports .NetStandard 2.0 (BTW the others don't, just realized that)
Turns out you have to install chrome into the docker image by putting the commands in the Docker file.
I've explained the whole learning process here including how I found this working code: https://hub.docker.com/r/masteroleary/selenium-dotnetcore2.2-linux
Since the appearance in dotnet core of self-contained applications I think a better approach is to use the official selenium docker:
https://hub.docker.com/r/selenium/standalone-chrome
and build the application self contained.
Here is my dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 as build-env
WORKDIR /app
COPY . ./
RUN dotnet publish MyApp.csproj -c Release -o out --self-contained -r linux-x64 /p:PublishTrimmed=true
FROM selenium/standalone-chrome
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["./MyApp"]
this is updated version for dotnet 6.0
also This is a multi stage Docker File and help with faster build
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
RUN apt update && apt install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
hicolor-icon-theme \
libcanberra-gtk* \
libgl1-mesa-dri \
libgl1-mesa-glx \
libpango1.0-0 \
libpulse0 \
libv4l-0 \
fonts-symbola \
--no-install-recommends \
&& curl -sSL https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& echo "deb [arch=amd64] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google.list \
&& apt-get update && apt-get install -y \
google-chrome-stable \
--no-install-recommends \
&& apt-get purge --auto-remove -y curl \
&& rm -rf /var/lib/apt/lists/*
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
/* The .net App Docker Configuration */
FROM build AS publish
RUN dotnet publish -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
RUN echo ls -a
ENTRYPOINT ["dotnet", "{Entry Point File Name}.dll"]

Convert HTML to PDF using wkhtmltopdf, docker in ASP.NET Core

I'm trying to convert HTML to PDF using wkhtmltopdf in my ASP.NET Core app.
I've added a wkhtmltopdf.exe to my project and marked as a Copy to an output to always.
Here's my code:
var htmlContent = receiptBody;
var wkhtmltopdf = new FileInfo(#"/app/Configuration/Wkhtmltopdf/wkhtmltopdf.exe");
var converter = new HtmlToPdfConverter(wkhtmltopdf);
var pdfBytes = converter.ConvertToPdf(htmlContent);
The file has been founded but on:
var pdfBytes = converter.ConvertToPdf(htmlContent);
I'm getting an error:
System.Exception: Cannot generate PDF: Broken pipe --->
System.IO.IOException: Broken pipe
I run my app using Docker and here is my dockerfile:
FROM microsoft/aspnetcore:1.1.2 ARG source WORKDIR /app
ENV ASPNETCORE_URLS http://project-test:80
EXPOSE 80
COPY ${source:-obj/Docker/publish} .
RUN apt-get update
RUN apt-get install-y libgdiplus
ENTRYPOINT ["dotnet", "ProjectTest.dll"]
Maybe should somehow install wkhtmltopdf for linux?
This piece of code works fine for me with the latest .net core 6.0.1 image:
FROM mcr.microsoft.com/dotnet/aspnet:6.0.1-bullseye-slim AS base
RUN apt update
RUN apt install -y libgdiplus
RUN ln -s /usr/lib/libgdiplus.so /lib/x86_64-linux-gnu/libgdiplus.so
RUN apt-get install -y --no-install-recommends zlib1g fontconfig libfreetype6 libx11-6 libxext6 libxrender1 wget gdebi
RUN wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.stretch_amd64.deb
RUN gdebi --n wkhtmltox_0.12.5-1.stretch_amd64.deb
RUN apt install libssl1.1
RUN ln -s /usr/local/lib/libwkhtmltox.so /usr/lib/libwkhtmltox.so
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0.1 AS build
WORKDIR /src
COPY ["docgen/docgen.csproj", "docgen/"]
RUN dotnet restore "docgen/docgen.csproj"
COPY . .
WORKDIR "/src/docgen"
RUN dotnet build "docgen.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "docgen.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "docgen.dll"]
It's all about platform and binaries.
You can generate your own docker image with WKHTMLTOPDF and use it.
In my opinion, it is a cleaner way.
Here is a simple example:
Download WkHtmlToPdf - Debian 10 (Buster) - amd64 file:
https://wkhtmltopdf.org/downloads.html
Put downloaded .deb file into "deps" subfolder below where the Dockerfile is
Add WkHtmlToPdf installation into the Dockerfile.
Make sure the download .deb filename matches the line:
ENV WKHTMLTOX wkhtmltox_0.12.6-1.buster_amd64.deb
And ensure the .NET Core images use Debian Buster.
Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /app
## copy csproj and restore as distinct layers
COPY *.sln .
COPY aspnetapp/*.csproj ./aspnetapp/
RUN dotnet restore
## copy everything else and build app
COPY aspnetapp/. ./aspnetapp/
WORKDIR /app/aspnetapp
RUN dotnet publish -c Release -o out
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS runtime
WORKDIR /app
## START - INSTALL WKHTMLTOPDF
ENV WKHTMLTOX wkhtmltox_0.12.6-1.buster_amd64.deb
ENV BUILD_PACKAGES build-essential
ENV MAIN_PACKAGES fontconfig libfreetype6 libjpeg62-turbo libxext6 libpng16-16 libx11-6 libxcb1 libxrender1 xfonts-75dpi xfonts-base
COPY deps/$WKHTMLTOX ./
RUN set -xe \
&& apt-get update -qq \
&& apt-get install --no-install-recommends -yq $BUILD_PACKAGES $MAIN_PACKAGES \
&& dpkg -i ${WKHTMLTOX} \
&& apt-get remove -y $BUILD_PACKAGES \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& rm -rf ${WKHTMLTOX} \
&& truncate -s 0 /var/log/*log
## END - INSTALL WKHTMLTPDF
COPY --from=build /app/aspnetapp/out ./
ENTRYPOINT ["dotnet", "aspnetapp.dll"]
The INSTALL WKHTMLTOPDF code is based on:
https://gist.github.com/berkayakcay/1e4f0a355437f0db9c94935aa52283d2
The Dockerfile itself is based on:
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/building-net-docker-images?view=aspnetcore-3.1#the-dockerfile-1
You should download files wkhtmltopdf for linux and copy to output publish.
Also, give permissions to these files with this step in dockerfile
RUN chmod 755 ./wkhtmltopdf/Linux/wkhtmltopdf
RUN chmod 755 ./wkhtmltopdf/Linux/wkhtmltoimage
Be sure that you add the line after WORKDIR/app:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /app
RUN apt-get update -qq && apt-get -y install libgdiplus libc6-dev
For more info check https://github.com/fpanaccia/Wkhtmltopdf.NetCore

Categories

Resources