AWS LambdaとDioDocsでExcelやPDFファイルを出力する(1)

本記事では、AWS Lambdaで「DioDocs(ディオドック)」を使用したC#( .NET Core 3.1)のLambda関数アプリケーションを作成し、ExcelやPDFファイルを出力する方法について紹介します。

AWS Lambdaとは

AWS LambdaはAmazon Web Servicesで提供されている、各種イベントをトリガーに処理を実行するサーバーレスなアプリケーションを作成できるクラウドサービスです。

AWS Lambdaは.NET Core 3.1をサポートしており、C#で .NET CoreベースのLambda関数を作成できます。今回はAWS Toolkit for Visual Studioを使用してVisual Studio 2019でLambda関数を作成し、AWSへデプロイして確認してみます。

実装する内容

今回実装する内容は非常にシンプルです。AWS LambdaアプリケーションでAmazon API GatewayからHTTPリクエストを受け取るLambda関数を作成します。この関数の実行時にDioDocsを使用してExcelとPDFファイルを作成し、HTTPリクエストのクエリパラメータで受け取った文字列を追加します。その後、作成したExcelとPDFファイルを関数からAmazon API Gatewayに渡してHTTPレスポンスで直接ローカルへ出力する、といった内容です。

AWS Toolkit for Visual Studioのセットアップ

Visual Studio 2019へのAWS Toolkit for Visual StudioのインストールとAWSの認証情報の設定は以下を参考にして準備しておきます。

AWS Toolkit for Visual Studio をセットアップする – AWS Toolkit for Visual Studio

AWS 認証情報を提供する – AWS Toolkit for Visual Studio

AWS Lambdaアプリケーションを作成

以下のドキュメントを参考にAWS Lambdaアプリケーションを作成していきます。

チュートリアル: AWS Toolkit for Visual Studio の AWS Lambda プロジェクトを使用する – AWS Toolkit for Visual Studio

Visual Studio 2019でプロジェクトテンプレート「AWS Lambda Project (.NET Core – C#)」を選択して[次へ]をクリックします。

AWS Lambdaアプリケーションを作成

プロジェクト名に「ExcelExportAWSLambda1」を入力して[作成]をクリックします。

AWS Lambdaアプリケーションを作成

AWS Lambda Projectのテンプレートを選択します。「Empty Function」を選択して[Finish]をクリックします。

AWS Lambdaアプリケーションを作成

「ExcelExportAWSLambda1」プロジェクトが作成されます。

AWS Lambdaアプリケーションを作成

NuGetパッケージを追加

Visual Studioの「NuGet パッケージ マネージャー」からAmazon API Gatewayのイベントを処理するためのパッケージ「Amazon.Lambda.APIGatewayEvents」とDioDocs for Excelのパッケージ「GrapeCity.DioDocs.Excel.ja」をインストールします。

NuGetパッケージを追加

Amazon API Gatewayを使うコードを追加

Lambda関数がAmazon API GatewayからHTTPリクエストを受け取り、Lambda関数からAPI GatewayへHTTPレスポンスを返すために、以下のようにFunctionHandlerの引数と戻り値にAPIGatewayProxyRequestAPIGatewayProxyResponseを設定します。

public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest input, ILambdaContext context)

DioDocs for Excelを使うコードを追加

DioDocs for ExcelでExcelファイルを作成するコードを追加してFunctionHandlerを以下のように更新します。

public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest input, ILambdaContext context)
{
    APIGatewayProxyResponse response;

    string queryString;
    input.QueryStringParameters.TryGetValue("name", out queryString);

    string Message = string.IsNullOrEmpty(queryString)
        ? "Hello, World!!"
        : $"Hello, {queryString}!!";

    //Workbook.SetLicenseKey("");

    Workbook workbook = new Workbook();
    workbook.Worksheets[0].Range["A1"].Value = Message;

    var base64String = "";

    using (var ms = new MemoryStream())
    {
        workbook.Save(ms, SaveFileFormat.Xlsx);
        base64String = Convert.ToBase64String(ms.ToArray());
    }

    response = new APIGatewayProxyResponse
    {
        StatusCode = (int)HttpStatusCode.OK,
        Body = base64String,
        IsBase64Encoded = true,
        Headers = new Dictionary<string, string> {
            { "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
            { "Content-Disposition", "attachment; filename=Result.xlsx"},
        }
    };    

    return response;
}

DioDocs for Excelで作成したExcelファイルをMemoryStreamに保存し、これを一旦base64エンコードしています。これを文字列base64StringとしてAPIGatewayProxyResponseBodyに設定してAmazon API Gatewayに渡すようにしています。

作成したExcelファイルをbase64にエンコードする理由ですが、Lambda関数とAmazon API Gatewayを連携させる「AWS Lambdaプロキシ統合」を利用する際の決まり事になっています。

AWS Lambda プロキシ統合からバイナリメディアを返すには、Lambda 関数からのレスポンスを base64 でエンコードします。また、API のバイナリメディアタイプを設定する必要があります。

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/lambda-proxy-binary-media.html

デバッグ実行で確認

作成したLambda関数アプリケーションをローカルでデバッグ実行して確認します。F5キーをクリックするとMock Lambda Test Toolが起動します。

デバッグ実行で確認

Example Requestsに「API Gateway AWS Proxy」を設定して[Exceute Function]をクリックします。

デバッグ実行で確認

Responseのbodyにbase64にエンコードされた文字列が格納されていればOKです。

デバッグ実行で確認

AWSへデプロイ

作成したLambda関数アプリケーションをAWSへデプロイして確認します。ソリューションエクスプローラーから「ExcelExportAWSLambda1」プロジェクトを右クリックして「Publish to AWS Lambda」を選択します。

AWSへデプロイ

「Function Name」にDioDocsExcelExportを入力して[Next]をクリックします。

AWSへデプロイ

「Role Name」にNew role based on AWS managed policy: AWSLambda_FullAccessを設定して[Upload]をクリックします。

AWSへデプロイ

成功すると以下の画面が表示されます。

AWSへデプロイ

AWSのコンソールでAWS Lambdaの「関数」を選択するとデプロイしたLambda関数「DioDocsExcelExport」が表示されます。

AWSへデプロイ

トリガーの追加

デプロイしたLambda関数「DioDocsExcelExport」をクリックして以下の画面から[トリガーを追加]をクリックします。

トリガーの追加

「API Gateway」を選択し、さらに「APIを作成する」を選択します。作成するAPIタイプは「REST API」を選択して、セキュリティは「オープン」を選択します。この状態で[追加]をクリックします。

トリガーの追加

以下のようにトリガーにAPI Gatewayが追加されます。

トリガーの追加

API Gatewayのバイナリメディアタイプを設定

AWSのコンソールで作成したAPI「DioDocsExcelExport-API」の「設定」からバイナリメディアタイプを「*/*」で追加します。追加後に[変更の保存]をクリックします。

API Gatewayのバイナリメディアタイプを設定

「*/*」を設定する理由ですが、 Lambda関数とAmazon API Gatewayを連携させる「AWS Lambdaプロキシ統合」を利用する際の決まり事になっています。

この統合例でウェブブラウザを使用して API を呼び出すには、API のバイナリメディアタイプを */* に設定します 。API Gateway は、クライアントからの最初の Accept ヘッダーを使用して、レスポンスがバイナリメディアを返すかどうかを判断します。ブラウザからのリクエストなど、Accept ヘッダー値の順序を制御できない場合に、バイナリメディアを返すには、API のバイナリメディアタイプを */* (すべてのコンテンツタイプ) に設定します。

https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/lambda-proxy-binary-media.html

この設定ですが、気を利かせたつもりでapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetなど、固有のバイナリメディアタイプを設定してしまうとbase64エンコードされたただの文字列が出力されてしまうので注意が必要です。

デプロイしたアプリケーションを確認

AWSのコンソールで作成したAPI「DioDocsExcelExport-API」の「リソース」から[アクション]をクリックし、「APIのデプロイ」を選択します。

デプロイしたアプリケーションを確認

デプロイされるステージは「default」を選択して[デプロイ]をクリックします。

デプロイしたアプリケーションを確認

API「DioDocsExcelExport-API」の「ステージ」から「default – GET – /DioDocsExcelExport」を選択します。

デプロイしたアプリケーションを確認

「URLの呼び出し」に表示されているAPIのURLをコピーしてブラウザに張り付けて、さらにクエリパラメータと文字列「?name=DioDocsForExcel」を追加します。

デプロイしたアプリケーションを確認

このAPIを実行するとクエリパラメータで渡した文字列「DioDocsForExcel」が追加されたExcelファイル「Result.xlsx」がローカルに出力されます。

デプロイしたアプリケーションを確認

PDFを出力するには?

Visual Studioの「NuGet パッケージ マネージャー」から DioDocs for PDFのパッケージ「GrapeCity.DioDocs.Pdf.ja」をインストールします。 DioDocs for PDFでPDFファイルを作成するコードを追加してFunctionHandlerを以下のように更新します。PDFファイルを出力するのでそれに合わせてAPIGatewayProxyResponseHeaderの内容も変更しています。

public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest input, ILambdaContext context)
{
    APIGatewayProxyResponse response;

    string queryString;
    input.QueryStringParameters.TryGetValue("name", out queryString);

    string Message = string.IsNullOrEmpty(queryString)
        ? "Hello, World!!"
        : $"Hello, {queryString}!!";

    //GcPdfDocument.SetLicenseKey("");

    GcPdfDocument doc = new GcPdfDocument();
    GcPdfGraphics g = doc.NewPage().Graphics;

    g.DrawString(Message,
        new TextFormat() { Font = StandardFonts.Helvetica, FontSize = 12 },
        new PointF(72, 72));

    var base64String = "";

    using (var ms = new MemoryStream())
    {
        doc.Save(ms, false);
        base64String = Convert.ToBase64String(ms.ToArray());
    }

    response = new APIGatewayProxyResponse
    {
        StatusCode = (int)HttpStatusCode.OK,
        Body = base64String,
        IsBase64Encoded = true,
        Headers = new Dictionary<string, string> {
            { "Content-Type", "application/pdf" },
            { "Content-Disposition", "attachment; filename=Result.pdf"},
        }
    };

    return response;
}

さいごに

動作を確認できるAWS Lambdaアプリケーションのサンプルはこちらです。

https://github.com/GrapeCityJP/ExcelExportAWSLambda1

https://github.com/GrapeCityJP/PDFExportAWSLambda1

本記事ではAWS LambdaとAmazon API Gatewayで作成したAPIを使用して直接ローカルへExcelとPDFファイルを出力する方法を紹介しましたが、ファイルの保存先としてAmazon S3を使うより実践的な方法もあります。こちらも今後の記事で紹介したいと思います。