Cómo convertir un documento de Office a PDF utilizando Azure Functions v2 y Docker

Existen varias bibliotecas de pago para .NET que permiten convertir cualquier documento de Office (Word, PowerPoint, …) en PDF de manera fácil y rápida. Sin embargo, en esta entrega aprenderemos cómo crear un servicio online de conversión de documentos a PDF, de manera gratuita (A ver, gratuito en esta vida no hay nada 😉, pero al menos en este ejemplo, solo pagarás el uso que le des a los servicios de Azure).
Puedes descargar el código fuente del proyecto de ejemplo desde Github

LibreOffice al rescate

LibreOffice es una suite de ofimática, gratuita, de código abierto, que permite manipular documentos en formato Microsoft Office (OpenXML). Tiene una herramienta de línea de comando que convierte cualquier documento (Word, PowerPoint, …) a PDF.

En Linux, el comando sería el siguiente:

/usr/bin/libreoffice --norestore --nofirststartwizard --headless --convert-to pdf source-file.docx

En Windows:

C:\Program Files\LibreOffice\program\soffice.exe -norestore -nofirststartwizard -headless -convert-to pdf source-file.docx

Esto creará un documento PDF, con el mismo nombre del fichero original, pero en la carpeta actual.

Entonces, ¿Por qué no encapsular LibreOffice como un servicio HTTP de conversión de documentos?

Este mecanismo es similar a utilizar la Automatización de Office en el servidor en Windows, pero con la diferencia de que al ser Linux y una herramienta de línea de comando, es más ligera. En cualquier caso, tenga en cuenta que cada llamada HTTP lanzaría un proceso de LibreOffice, por lo que habría un límite máximo de conversiones en paralelo según la RAM y el CPU del que dispongamos.

Creando el servicio de conversión utilizando una función de Azure con un desencadenador HTTP (HTTP Trigger)

La idea es crear una función de Azure que haga lo siguiente:

  1. Reciba una URL y descargue el documento que se quiere convertir (En nuestro ejemplo, recibiremos la URL como parte del POST)
  2. Lance el comando de LibreOffice y realice la conversión a PDF
  3. Almacene el resultado en un Azure Blob Storage
  4. Retorne una URL segura para descargar el documento convertido a PDF

El código completo de la función se puede encontrar aquí. Sin embargo, la parte más importante es esta:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Convert file to PDF using libreoffice
var pdfProcess = new Process();
pdfProcess.StartInfo.FileName = LIBRE_OFFICE_BIN;
pdfProcess.StartInfo.Arguments = $"--norestore --nofirststartwizard --headless --convert-to pdf \"{sourceFileName}\"";
pdfProcess.StartInfo.WorkingDirectory = Path.GetDirectoryName(sourceFileName); //This is really important
pdfProcess.Start();

// Wait while document is converting
while (pdfProcess.IsRunning())
{
	await Task.Delay(500);
}

// Check if file was converted properly
var destinationFileName = $"{Path.Combine(Path.GetDirectoryName(sourceFileName), Path.GetFileNameWithoutExtension(sourceFileName))}.pdf";
if (!File.Exists(destinationFileName))
{
	return new BadRequestObjectResult("Error converting file to PDF");
}

Incluso, podemos ejecutar esta función tanto en Windows como en Linux, cambiando la ruta en donde se encuentra LibreOffice.

1
2
3
4
5
#if DEBUG
        const string LIBRE_OFFICE_BIN = @"C:\Program Files\LibreOffice\program\soffice.exe";
#else        
        const string LIBRE_OFFICE_BIN = "/usr/bin/libreoffice";
#endif

Hasta ahora, todo bien. Pero… ¿Cómo instalamos LibreOffice en un entorno sin servidor (Serverless) en Azure?

Tenga en cuenta que en un entorno Serverless no tenemos acceso al servidor ni control sobre los programas que están instalados. La función de azure ejecutará en “algún lugar” de Azure, incluso, en ocasiones, en un servidor diferente.

Docker al rescate

Por suerte, las Azure Functions v2 ejecutan en Linux y podemos crear un contenedor docker con todas las herramientas que necesitemos ❤️. El motor de ejecución de Azure Functions v2 para Linux está basado en Debian stretch por lo que podemos instalar LibreOffice sin problema ninguno:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
FROM mcr.microsoft.com/azure-functions/dotnet:2.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:2.1-stretch AS build
WORKDIR /src
COPY ["PdfConverterFunction.csproj", ""]
RUN dotnet restore "./PdfConverterFunction.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "PdfConverterFunction.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "PdfConverterFunction.csproj" -c Release -o /home/site/wwwroot

FROM base AS final
WORKDIR /home/site/wwwroot
COPY --from=publish ["/home/site/wwwroot", "/home/site/wwwroot"]
ENV AzureWebJobsScriptRoot=/home/site/wwwroot
RUN mkdir -p /usr/share/man/man1
RUN apt-get update && apt-get upgrade -y
RUN apt-get -y install default-jre-headless libreoffice

Para crear el contenedor y probarlo localmente, ejecutamos los siguientes comandos:

1
2
docker build . -t pdfconverter
docker run -p 80:80 -e AzureWebJobsStorage="<your-blob-connection-string>" --name pdfconverter pdfconverter

Esto hospedará la función y la hará accesible por el puerto 80. Si todo ha ido bien, al acceder a http://localhost debe visualizarse una imagen como esta:

Figura 1 - Azure Function Runtime página principal

Para probar la conversión a PDF, realice un POST similar a este:

1
2
3
4
5
POST http://localhost/api/PdfConverterFunction

{
	"url": "https://server/path/document.docx"
}

La función deberá retornar una URL con la cual descargar el documento PDF convertido.

Publicando en Azure

Una vez que se tenga creado el contenedor, se puede publicar en azure gracias al servicio Web App for Containers que permite la ejecución de aplicaciones basadas en Linux (en especial, las creadas con .NET Core).

Para realizar la publicación, hay que seguir los siguientes pasos:

  1. Subir la imagen al Azure Container Registry o a Docker Hub

  2. Crear un Function App y seguir las instrucciones del asistente (se podrá seleccionar Linux y desplegar directamente el contenedor previamente creado)

Probablemente, si se utilizan las bibliotecas de pago disponibles para .NET Core, este proceso de conversión de documentos de Office a PDF sea mucho más eficiente y sencillo, pero esta vía, utilizando Azure Functions, Docker y LibreOffce, seguro que algo hace 😂.

Compartir con:

Alejandro Tamayo Castillo

Profesor, Investigador, Arquitecto de software, Consultor y entusiasta de la tecnología. Master of Science (MSc) in Computer Science actualmente trabajando con tecnología full-stack de Microsoft y otros fabricantes, como .NET Core, ASP.NET Core, Azure, Serverless, SPA/ReactJS, HTML5, Xamarin (Android/iOS/Forms), Orchard, SharePoint, HoloLens, Bots, y otros temas chulos.

Seguir a Alejandro Tamayo Castillo en LinkedIn