Blazor WebAssemblyでAWS SDKを使用しようとしてハマった話
2021.12.27
この記事は最終更新日から1年以上が経過しています。
どもです。
WebAssemblyにWebの未来を感じておりまして、社内カンファレンスでも大いにお話させていただいたのですが、今回Blazor WebAssemblyでAWS SDKを使用しようとしてハマっちゃったよぉと言ったお話です。
検証のプロジェクトで、ファイルをS3にアップロードしたかったので、AWS JavaScript SDKを使う様な感覚で、検証のプロジェクトだからAWS SDK の S3 をフロント側で利用してファイルをアップロードしようと試みてみました。
AWSのキーなどをフロント側で持つ事になりますが、検証のプロジェクトで公開予定もないのでその辺は良いと言うことでサクッと作成しようと思っていたのですが。。。
まずは、AWS SDKでサクッとファイルアップロード
node.jsで実装する際は、以下の様なソースコードでS3へのアップロードは可能だと思います。
const AWS = require('aws-sdk'); const fs = require('fs'); // accessKeyId、secretAccessKeyが記載されたファイル読み込み AWS.config.loadFromPath('./awskey.json'); AWS.config.update({region: 'AWS region'}); const s3 = new AWS.S3(); // 上記のAWS.configでkey読み込まずにベタ書きの場合 // import { S3Client } from '@aws-sdk/client-s3'; // const s3 = new S3Client({ // region: 'ap-northeast-1 などAWS region', // credentials: { // accessKeyId: 'accessKeyId', // secretAccessKey: 'secretAccessKey', // } // }); const params = { Bucket: "Bucket名", Key: "s3 Bucketkey(ファイル・ディレクトリ・パス)" }; const file = fs.readFileSync("ファイルパス"); params.Body = file; s3.putObject(params, function(err, data) { if (err) console.log(err, err.stack); else console.log(data); });
こんな感じにサクッといきたかったので調べていたのですが、どうもBlazorのユーザーが少ないのか、日本語記事での情報が少ない。。
多くは海外の情報となってしまう。
色々と参考にしながら、とりあえずはAWS SDKが必要なので、NuGetに取得することに。
Visual StudioのNuGetで右クリックで、「Manage NuGet Packages…」を選択。
「AWSSDK.Core」と「AWSSDK.S3」をインストール。
ひょっとしたら、「AWSSDK.S3」ないかも。。(多分いる)
「_imports.razor」に以下を記述。
_imports.razor
@using Amazon @using Amazon.S3 @using Amazon.S3.Model
.NetCore5より「InputFile」コンポーネントが用意されており、inputフォームを使用する際は、こちらを使うと良いみたいなので、使用していく。
<InputFile OnChange="@LoadFiles" multiple /> <button type="submit" @onclick="SubmitFileAsync">Upload</button>
C#のコードは、最小限にするとして以下の様な感じで作成。
@code { private AmazonS3Client s3Client; private const string BUCKET_NAME = "S3のバケット名"; private const string FOLDER_NAME = "ディレクトリ名"; private const double DURATION = 24; private Stream _fileStream = null; private string _selectedFileName = null; private string _objectKey = null; private string _contentType = null; protected override async Task OnInitializedAsync() { var config = new AmazonS3Config { RegionEndpoint = RegionEndpoint.APNortheast1, UseAlternateUserAgentHeader = true }; s3Client = new AmazonS3Client("AWS accessKeyId", "AWS secretAccessKey", config); await JSRuntime.InvokeAsync<string>("console.log", s3Client); } public void LoadFiles(InputFileChangeEventArgs e) { try { foreach (var file in e.GetMultipleFiles(3)) { StateHasChanged(); using (Stream stream = file.OpenReadStream()) { _fileStream = stream; _selectedFileName = file.Name; _objectKey = $"{FOLDER_NAME}/{file.Name}"; _contentType = file.ContentType; } } } catch (Exception ex) { Console.WriteLine(ex); } } public async Task SubmitFileAsync() { var putObjectRequest = new PutObjectRequest { BucketName = BUCKET_NAME, Key = _objectKey, InputStream = _fileStream, ContentType = _contentType, }; var response = await s3Client.PutObjectAsync(putObjectRequest); } }
それでは、実行してみると。
ファ。エラー
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: System.Security.Cryptography.Algorithms is not supported on this platform. System.PlatformNotSupportedException: System.Security.Cryptography.Algorithms is not supported on this platform. at System.Security.Cryptography.HMACSHA256..ctor() at
エラーを見てみると、
System.Security.Cryptography.Algorithms is not supported on this platform.
だと。。。
どうやら、AWS SDK PutObjectRequest class使用時にエラーがスローされている様子。
AWS SDK PutObjectRequest class
WebAssembly上でのエラーなので調べていると、公式ドキュメントの方には以下の様に記述されている。
System.Security.Cryptography API は、ブラウザーで実行されると、 実行時に PlatformNotSupportedException をスローします。
あぁ。それだ。対応策はと。。
推奨アクション 現時点では、推奨される回避策はありません。
ガーン。。。
BlazorでのAWS SDK使用は対応しているって書いていたはずなのに。。。
よくよく見ていると。。redditにて以下の様なコメントを見つけた。。
みなさん、こんにちは。最新リリース(v3.5.2)では、Blazor WebAssembly ランタイムをサポートするようになりました! SDK チームに感謝します 注:私はAmazon Web Servicesの社員です(たまたまC#が好きなんです!)。 EDIT: リリース後のテストで、Blazor WASMランタイムの.NET 5バージョンに破損があることがわかりました。 System.Security.Cryptography.Algorithms名前空間へのサポートが削除され、ほぼすべてのサービスクライアントが使用しているSigv4署名が失敗することになります。 net core app3.1ランタイムは動きますが。
ガーン。。
でも、「net core app3.1ランタイムは動きますが。」と。
という事で、.NET core 3.1で実行していくことに決めたのでやってみることに。
.NET core 3.1では、.NET core 5で使用できるようになったInputFileコンポーネントは使用できない(組み込まれていない)ので、オープンソースの「https://github.com/SteveSandersonMS/BlazorInputFile」をNuGetパッケージで取得して使ってみることに。
インストールしたら、「_imports.razor」にBlazorInputFileを追加。
@using BlazorInputFile;
コンポーネントは、.NET core 5で使用できるようになったInputFileコンポーネントとほぼ同様。
<InputFile OnChange="HandleSelection" />
サンプルソースを参考に、MemoryStream参照のため「System.IO」をusing指定。
@using System.IO;
statusを表示するためのHTMLを記述。
<p>@status</p>
C#のcode箇所。サンプルソース参考。
@code { string status; async Task HandleSelection(IFileListEntry[] files) { var file = files.FirstOrDefault(); if (file != null) { // Just load into .NET memory to show it can be done // Alternatively it could be saved to disk, or parsed in memory, or similar var ms = new MemoryStream(); await file.Data.CopyToAsync(ms); status = $"Finished loading {file.Size} bytes from {file.Name}"; } } }
という事で実行してみると。
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Could not find 'BlazorInputFile' in 'window'. Error: Could not find 'BlazorInputFile' in 'window'.
ブヘー。。windowオブジェクトにBlazorInputFileとかないよエラー。。
「https://github.com/SteveSandersonMS/BlazorInputFile」をフォークした「 Agno.BlazorInputFile」というプロジェクトがあり、もしかしたら色々対応しているかもと試してみることに。
using指定は以下のように「Agno.BlazorInputFile」となります。
@using Agno.BlazorInputFile;
コンポーネントは以下の様になります。
<AgnoInputFile OnFileChange="HandleSelection" />
使用してみると。
Unhandled exception rendering component: Could not find 'BlazorInputFile' in 'window'. Error: Could not find 'BlazorInputFile' in 'window'.
ぶへー同じエラー。。
もう、わかんねーよー。
というか、レポジトリ見てみるとわかりますが、.NET core 5で使用できるようになっているのでもう使わないでねぇ〜。と記述されているので、そもそも使用しないほうが良いな。。
という事で、色々やった結果、WebAssemblyのガッツリしたセキュリティもあってか、Blazor WebAssemblyのフロントのみのAWS SDK S3アップロードは厳しい感じがありますので、公式ドキュメントに沿って、通常の**.NET core 5 + サーバーサイド(lamdba などサーバーレス)**と言った形にすることにしました。
海外の記事ではありますが、Blazor WebAssemblyでのAWS SDK使用例があったりして参考にしましたが、どうやら**.NET core 5**以前のバージョンの様であって、上手く行かなかったです。。
という事で、次回は、公式ドキュメントに沿ってのアップロードを試して行きたいと思います。
ではではぁ。
またまたぁ。