はじめに
個人プロジェクトで C# の Lamda 関数を使う予定なのですが、いやぁ情報が少ないですね。
今回も S3 にアップロードした Excel ファイルを読み込もうとしたのですが、そもそも S3 のファイルを C# で扱う方法がほとんど見つからない。💦
まあ、そんなこんなですが、一応動くようになったので、C# の Lambda 関数で Excel ファイルを S3 から読み込む方法をご紹介します。
Excel の読み込みに使うライブラリ
C# で Excle ファイルを読み込むライブラリには ClosedXML と NPOI がありますが、今回は NPOI で試してみます。
理由は、ClosedXML は使いやすいようですが、xls 形式に対応していないのですよね。
一方、NPOI は古くからあるプロジェクトなので、xls, xlsx 両形式に対応しています。
個人プロジェクトは xls, xlsx 両形式をサポートする予定なので、NPOI を選択した次第です。
ClosedXML, NPOI については以下の記事が詳しいので参照してください。
C# で Lambda 関数を作成するために
C# で Lambda 関数を作成するために、Visual Studio 2017 Community Edition と AWS Toolkit for Visual Studio を使用しました。
NPOI は NuGet からインストールすることが可能です。
今回は S3 を使用するので、NuGet で AWSSDK.S3 というライブラリをインストールする必要があります。
NPOI と AWSSDK.S3 をインストールしたプロジェクトになりますので、関連ライブラリを ZIP ファイルにまとめてサーバーにアップロードする必要があります。こちらについては、以下の記事を参照してください。
S3 に Excel ファイルをアップロードする
まず、Excel ファイルを S3 にアップロードする必要があります。
東京リージョンにバケットを作成して、Excel ファイルは test.xlsx という名前でアップロードします。
Excel ファイルの中身は以下の通りです。
ここでバケットを他のリージョンに作成すると、以下のようなエラーが発生して頭を抱えることになるのでご注意ください。
The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.
C# のソースコード
いよいよ C# のソースコードになります。
以下のように Lambda 関数を記述します。
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.IO; using System.Threading.Tasks; using NPOI.SS.UserModel; using Amazon.Lambda.Core; using Amazon.S3; [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] namespace AWSLambda1 { public class Function { public async Task FunctionHandler(object input, ILambdaContext context) { // S3のリージョンを指定する AmazonS3Client s3 = new AmazonS3Client(Amazon.RegionEndpoint.APNortheast1); var res = await s3.GetObjectAsync( bucketName: "BucketName", key:"test.xlsx" ); // 日時をファイル名につけるの重要! var filePath = "/tmp/" + (DateTime.Now).ToString("yyyyMMdd_HHmmss") + "_test.xlsx"; await res.WriteResponseStreamToFileAsync(filePath, false,System.Threading.CancellationToken.None); // Create で xls,xlsx に対応 IWorkbook workbook = WorkbookFactory.Create(filePath); ISheet worksheet = workbook.GetSheetAt(0); // 最終行数を取得 int lastRow = worksheet.LastRowNum; for (int i = 0; i <= lastRow; i++) { // 行取得 IRow row = worksheet.GetRow(i); // セルの値取得 ICell cell1 = row?.GetCell(0); ICell cell2 = row?.GetCell(1); // ログに出力 LambdaLogger.Log(cell1?.StringCellValue); LambdaLogger.Log((cell2?.NumericCellValue).ToString()); } // ファイル削除 File.Delete(filePath); } } }
基本的にコメントに書いてある通りなのですが、いくつか補足をします。
AmazonS3Client
をインスタンスするときに、リージョンの指定をしています。必ずしも必要な訳ではないですが、エラーが発生する場合は指定してください。
/tmp
にファイルをコピーする時にファイル名に日時を追加していますが、これは/tmp
がコンテナで共通のディレクトリになるからです。ファイル名固定で処理を行うと、同時に他のユーザーが実行しているとエラーが発生する可能性があります。
なお、この/tmp
ディレクトリは上限が512MBになっているのでご注意ください。
workbook をインスタンスする際にWorkbookFactory.Create
を使用していますが、これで xls, xlsx 両形式に対応することができます。
最後に/tmp
にコピーしたファイルを削除しています。これをやらないと処理を行う度に/tmp
ディレクトリにファイルが増えていってしまいます。
実行結果
この Lambda 関数をサーバーにアップロードした結果は成功で、CloudWatch に以下のログが出力されたので想定通りになります。
START RequestId: b9bc1eca-36e2-417b-a899-d7a6ef705f57 Version: $LATEST AAAAA 11111 BBBBB 22222 CCCCC 33333 END RequestId: b9bc1eca-36e2-417b-a899-d7a6ef705f57
おわりに
毎度ながら情報が少なく苦労させられるのですが、この情報がお役に立てば幸いです。