From my (somewhat limited) experience PdfSharp / MigraDoc seems like a pretty fine and powerful library for creating .pdf documents, but it’s documentation - not so much. It’s a bit all over the place and with multiple different NuGet versions/builds and outdated StackOverflow code samples not really helping the situation.
However, creating a .pdf document in-memory and uploading it straight to Azure is not really that complicated. When might this be useful? For example when you need to generate a report but instead of immediately giving it to the user it just needs to get stored for later access.
Magic word we’re looking for is MemoryStream
. We’ll use two classes - one which will take a MemoryStream
and upload it to Azure (AzureProvider.cs
), and another one which will create a very simple MigraDoc document (ReportProvider.cs
) which you can then build upon and then feed that document to the AzureProvider
in the form of MemoryStream
.
The code is pretty straightforward and looks like this:
// AzureProvider.cs
using System;
using System.Configuration;
using System.IO;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
namespace MyApp.Providers
{
public class AzureProvider
{
public async Task<string> UploadStreamToAzure(string containerName, string fileName, MemoryStream stream)
{
var blockBlob = ResolveCloudBlockBlob(containerName, fileName);
await blockBlob.UploadFromStreamAsync(stream);
return fileName;
}
public CloudBlockBlob ResolveCloudBlockBlob(string containerName, string fileName)
{
var container = ResolveCloudBlobContainer(containerName);
var blockBlob = container.GetBlockBlobReference(fileName);
return blockBlob;
}
public CloudBlobContainer ResolveCloudBlobContainer(string containerName)
{
var storageAccount = GetCloudStorageAccount();
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(containerName);
return container;
}
private CloudStorageAccount GetCloudStorageAccount()
{
return CloudStorageAccount.Parse(ResolveAzureStorageConnectionString());
}
public string ResolveAzureStorageConnectionString()
{
var accountName = ConfigurationManager.AppSettings["Azure.Storage.AccountName"]; // Get account name from web.config
var accessKey = ConfigurationManager.AppSettings["Azure.Storage.PrimaryAccessKey"]; // Get primary access key from web.config
return String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}", accountName, accessKey);
}
}
}
// ReportProvider.cs
using System.IO;
using System.Threading.Tasks;
using MigraDoc.DocumentObjectModel;
using MigraDoc.Rendering;
using PdfSharp.Pdf;
namespace MyApp.Providers
{
public class ReportProvider
{
protected AzureProvider _azureProvider;
public ReportProvider()
{
_azureProvider = new AzureProvider();
}
public async Task<string> CreateReport()
{
var document = CreateDocument();
var containerName = "democontainer";
var fileName = "demodocument.pdf";
var stream = ConvertMemDocToMemoryStream(document);
return await _azureProvider.UploadStreamToAzure(containerName, fileName, stream);
}
private Document CreateDocument()
{
Document document = new Document();
var section = document.AddSection();
var paragraph = section.AddParagraph("Report Demo");
// Add other document elements here
return document;
}
protected static MemoryStream ConvertMemDocToMemoryStream(Document document)
{
PdfDocumentRenderer pdfRenderer = new PdfDocumentRenderer(false, PdfFontEmbedding.Always);
pdfRenderer.Document = document;
pdfRenderer.RenderDocument();
MemoryStream stream = new MemoryStream();
pdfRenderer.PdfDocument.Save(stream, false);
return stream;
}
}
}
Somewhat related - in the next post I’ll explain how to stream a file from Azure Storage private container through .net WebAPI to an AngularJS app.