GroupDocs.Viewer throws an exception when rendering pdf/xlxs/ppt in a Docker container

Hi,

I am evaluating this product for my company at the moment. I have written a small application to render pdf, excel and powerpoint documents. However, when the app tries to render the document, it throws the below exception for pdf. Note, I have run the same code targeting IIS and it works fine.

GroupDocs.Viewer.Exceptions.GroupDocsViewerException
  HResult=0x80131500
  Message=Object reference not set to an instance of an object.
  Source=GroupDocs.Viewer
  StackTrace:
   at   .      (Int32 , IPageStreamFactory )
   at   .(Int32[] ,    , HtmlViewOptions )
   at   .      (Func`2 , ViewOptions , Int32[] )
   at   .      (Func`2 , ViewOptions )
   at GroupDocs.Viewer.Viewer.View(ViewOptions options)
   at DocumentService.DocumentViewer.LoadStream(String filePath) in C:\Users\61402\source\repos\API.DocumentViewer\DocumentService\DocumentViewer.cs:line 50
   at API.DocumentViewer.Controllers.DocumentViewerController.LoadStream(String filePath) in C:\Users\61402\source\repos\API.DocumentViewer\API.DocumentViewer\Controllers\DocumentViewerController.cs:line 25
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()

  This exception was originally thrown at this call stack:
      .      (int, GroupDocs.Viewer.Interfaces.IPageStreamFactory)
      .(int[],   , GroupDocs.Viewer.Options.HtmlViewOptions)
      .      (GroupDocs.Viewer.Common.Func<GroupDocs.Viewer.Options.ViewOptions,   >, GroupDocs.Viewer.Options.ViewOptions, int[])
      .      (GroupDocs.Viewer.Common.Func<GroupDocs.Viewer.Options.ViewOptions,   >, GroupDocs.Viewer.Options.ViewOptions)
    GroupDocs.Viewer.Viewer.View(GroupDocs.Viewer.Options.ViewOptions)
    DocumentService.DocumentViewer.LoadStream(string) in DocumentViewer.cs
    API.DocumentViewer.Controllers.DocumentViewerController.LoadStream(string) in DocumentViewerController.cs
    Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(object, object[])
    Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(Microsoft.AspNetCore.Mvc.Infrastructure.IActionResultTypeMapper, Microsoft.Extensions.Internal.ObjectMethodExecutor, object, object[])
    Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
    ...
    [Call Stack Truncated]

My code which renders the document follows:

 public string LoadStream(string filePath)
        {
            List<MemoryStream> pages = new List<MemoryStream>();

            using (Viewer viewer = new Viewer(filePath))
            {
                MemoryPageStreamFactory pageStreamFactory = new MemoryPageStreamFactory(pages);

                ViewOptions viewOptions =
                    HtmlViewOptions.ForEmbeddedResources(pageStreamFactory);

                viewer.View(viewOptions);
            }

            var stringContent = new StringBuilder();
            foreach (var page in pages)
            {
                stringContent.Append(Encoding.UTF8.GetString(page.ToArray()));
            }

            return stringContent.ToString();
        }


internal class MemoryPageStreamFactory : IPageStreamFactory
        {
            private readonly List<MemoryStream> _pages;

            public MemoryPageStreamFactory(List<MemoryStream> pages)
            {
                _pages = pages;
            }

            public Stream CreatePageStream(int pageNumber)
            {
                MemoryStream pageStream = new MemoryStream();

                _pages.Add(pageStream);

                return pageStream;
            }

            public void ReleasePageStream(int pageNumber, Stream pageStream)
            {
                //Do not release page stream as we'll need to keep the stream open
            }
        }

@sandieng

Could you please share the sample application and Dockerfile?

Hi Vladimir, please look at my code in Github.
Note, it will throw exception in Docker for file types: pdf, xls, xlxs, ppt. These types are working if run in IIS Express.

@sandieng

Thank you for providing the code.

When running on Linux there is a list of dependencies that should be installed that includes libgdiplus, Microsoft True Type Core Fonts ttf-mscorefonts-installer, and SkiaSharp.NativeAssets.Linux NuGet package as it is required by SkiaSharp.

The dockerfile_and_csproj.zip (1.6 KB) archive contains updated Dockerfile and API.DocumentViewer.csproj files.

In the API.DocumentViewer.csproj file the following line has been added:

<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.80.2" />

And here is the complete listing of the Dockerfile

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:3.1-bionic AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

# begin install libgdiplus and dependencies
RUN apt-get update \
    && apt-get install -y \
        apt-transport-https \
        dirmngr \
        gnupg \
        ca-certificates

RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF \
    && echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" >> /etc/apt/sources.list.d/mono-official-stable.list

RUN apt-get update \
    && apt-get install -y --allow-unauthenticated \
        libc6-dev \
        libgdiplus \
        libx11-dev
# end install libgdiplus and dependencies

# begin ttf-mscorefonts-installer
RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" | debconf-set-selections \
	&& apt-get update \
    && apt-get install -y \
        libfontconfig1 \
        xfonts-utils \
		ttf-mscorefonts-installer
# end ttf-mscorefonts-installer

FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["API.DocumentViewer/API.DocumentViewer.csproj", "API.DocumentViewer/"]
COPY ["DocumentService/DocumentService.csproj", "DocumentService/"]
RUN dotnet restore "API.DocumentViewer/API.DocumentViewer.csproj"
COPY . .
WORKDIR "/src/API.DocumentViewer"
RUN dotnet build "API.DocumentViewer.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "API.DocumentViewer.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "API.DocumentViewer.dll"]

You may find that the base image mcr.microsoft.com/dotnet/aspnet:3.1 has been changed to mcr.microsoft.com/dotnet/aspnet:3.1-bionic it’s just because we do testing with this image.

Hi Vladimir,

Thank you for your reply. I rerun my app with your changed csproj and dockerfile. However, I encounter a new error while attempting to run. Please note, I am running docker in Windows targeting Linux.
As I am not familiar with Docker, I am not sure if the commands you provided are targeting Ubuntu and not Windows, hence causing the error. I wonder if you can help. Thanks.

Below is the error I received while building the app:

Severity	Code	Description	Project	File	Line	Suppression State
Error	CTC1014	Docker command failed with exit code 1.
#1 [internal] load build definition from Dockerfile
#1 sha256:d8bf73cf2d18966cea710d3c8afe10052151fceeabd5e5cabc965c203b67d9c9
#1 transferring dockerfile: 1.83kB done
#1 DONE 0.0s

#2 [internal] load .dockerignore
#2 sha256:554ca241db13c0afb9ed389f3dd89caac3fb81035d5c043c45acb25b99cddd19
#2 transferring context: 35B done
#2 DONE 0.0s

#3 [internal] load metadata for mcr.microsoft.com/dotnet/aspnet:3.1-bionic
#3 sha256:f26fb89aa6f619219683479c22b7d1414440f816a04c2e68caa752d209083d38
#3 DONE 0.0s

#4 [base 1/6] FROM mcr.microsoft.com/dotnet/aspnet:3.1-bionic
#4 sha256:c1e981604230e29dd4352459446c14fe01e5419913716c5b5b2eaacfb6275f19
#4 DONE 0.0s

#5 [base 2/6] WORKDIR /app
#5 sha256:250ad48741776858ab421d97e87c2b9fc0f718bdd772c9243165114e53a20803
#5 CACHED

#6 [base 3/6] RUN apt-get update     && apt-get install -y         apt-transport-https         dirmngr         gnupg         ca-certificates
#6 sha256:bf4d5a8d34c0060913bd403182aa0ddbe05a187f43f90e0f5985b85bbc958b40
#6 0.836 Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
#6 1.144 Get:2 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
#6 2.933 Get:3 http://archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
#6 3.544 Get:4 http://archive.ubuntu.com/ubuntu bionic-backports InRelease [74.6 kB]
#6 4.153 Get:5 http://archive.ubuntu.com/ubuntu bionic/main amd64 Packages [1344 kB]
#6 10.47 Get:6 http://archive.ubuntu.com/ubuntu bionic/multiverse amd64 Packages [186 kB]
#6 11.33 Get:7 http://archive.ubuntu.com/ubuntu bionic/restricted amd64 Packages [13.5 kB]
#6 11.40 Get:8 http://archive.ubuntu.com/ubuntu bionic/universe amd64 Packages [11.3 MB]
#6 65.19 Reading package lists...
#6 65.84 E: Release file for http://security.ubuntu.com/ubuntu/dists/bionic-security/InRelease is not valid yet (invalid for another 1d 18h 13min 12s). Updates for this repository will not be applied.
#6 65.84 E: Release file for http://archive.ubuntu.com/ubuntu/dists/bionic-updates/InRelease is not valid yet (invalid for another 1d 18h 14min 0s). Updates for this repository will not be applied.
#6 65.84 E: Release file for http://archive.ubuntu.com/ubuntu/dists/bionic-backports/InRelease is not valid yet (invalid for another 1d 18h 15min 6s). Updates for this repository will not be applied.
#6 ERROR: executor failed running [/bin/sh -c apt-get update     && apt-get install -y         apt-transport-https         dirmngr         gnupg         ca-certificates]: exit code: 100
------
 > [base 3/6] RUN apt-get update     && apt-get install -y         apt-transport-https         dirmngr         gnupg         ca-certificates:
------
executor failed running [/bin/sh -c apt-get update     && apt-get install -y         apt-transport-https         dirmngr         gnupg         ca-certificates]: exit code: 100	API.DocumentViewer	C:\API.DocumentViewer\API.DocumentViewer\Dockerfile	1

Hi @sandieng

It seems to be a time or timezone issue, try restarting your docker host as proposed in this answer or try adding RUN apt-get -o Acquire::Check-Valid-Until=false -o Acquire::Check-Date=false update after EXPOSE 443 line in the `Dockerfile as it proposed in this answer.

Hi Vladimir,

Thank you for getting back to me. I’ve been busy with some other tasks. I am keen to see my POC app working with Docker in Windows targeting Linux.

I tried both your suggestions, but I still get the same error. I have pasted the DockerFile with all the changes you have recommended. I am hoping you can help me sort out the issue so I can evaluate the product properly. Thanks once again for your help.

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:3.1-bionic AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

# begin install libgdiplus and dependencies
RUN apt-get -o Acquire::Check-Valid-Until=false -o Acquire::Check-Date=false update

RUN apt-get update \
    && apt-get install -y \
        apt-transport-https \
        dirmngr \
        gnupg \
        ca-certificates

RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF \
    && echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" >> /etc/apt/sources.list.d/mono-official-stable.list


RUN apt-get update \
    && apt-get install \
        libc6-dev \
        libgdiplus \
        libx11-dev
# end install libgdiplus and dependencies

# begin ttf-mscorefonts-installer
RUN echo "ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true" | debconf-set-selections \
	&& apt-get update \
    && apt-get install -y \
        libfontconfig1 \
        xfonts-utils \
		ttf-mscorefonts-installer
# end ttf-mscorefonts-installer

FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
WORKDIR /src
COPY ["API.DocumentViewer/API.DocumentViewer.csproj", "API.DocumentViewer/"]
COPY ["DocumentService/DocumentService.csproj", "DocumentService/"]
RUN dotnet restore "API.DocumentViewer/API.DocumentViewer.csproj"
COPY . .
WORKDIR "/src/API.DocumentViewer"
RUN dotnet build "API.DocumentViewer.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "API.DocumentViewer.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "API.DocumentViewer.dll"]

@sandieng

Could you please run the following commands in the PowerShell to make sure that the host time is the same as time in a container

(Get-Date).ToUniversalTime()

docker run --rm -it mcr.microsoft.com/dotnet/aspnet:3.1-bionic /bin/bash -c 'date'

The output is expected to be like this local_and_container_date.png

Hi Vladimir,

Running these 2 commands in PowerShell work. Thank you for your help.

(Get-Date).ToUniversalTime()
docker run --rm -it mcr.microsoft.com/dotnet/aspnet:3.1-bionic /bin/bash -c 'date'

Cheers,
Sandi

@sandieng

You’re welcome!

Hi Vladimir,

A quick question. The evaluation software is only rendering 2 pages of document, is that right?
I did some testing with a PDF document which has 4 pages. After calling GroupDocs.Viewer, I can only see 2 page.

Cheers,
Sandi

@sandieng

That’s right! The evaluation version is only rendering 2 pages of a document. A temporary license can be requested at Temporary License - Purchase - groupdocs.com.