FastifyでNode.jsのファイルアップロードを処理する方法
Amarachi Aso
2022年12月8日
0 分で読めますファイルアップロードは、Webアプリケーションでよく必要とされる機能です。そのため、多くのWebフレームワークには、サーバーサイドのアプリケーションでファイルを受け取り、扱うための仕組みが用意されています。Node.jsのWebフレームワークであり、Expressの後継と見なされることも多いFastifyも同様です。高速で、強力なプラグインアーキテクチャを備え、オーバーヘッドを最小限に抑えながら、優れた開発者体験の提供に重点を置いています。
このチュートリアルでは、Node.jsアプリケーションでFastifyフレームワークを使ってファイルアップロードを設定します。一般的なサーバーでファイルアップロードがどのように機能するか、Fastifyサーバーアプリケーションでアップロードを処理する方法を学びます。
ファイルアップロードの基礎
アプリケーションサーバーにファイルをアップロードするには、クライアントのコンピューターからサーバーへPOSTリクエストを送信する必要があります。リクエストボディには、通常HTMLフォームから送信されるデータを含めます。HTMLフォームで使用できるデータのエンコード方法は3種類あります。
application/x-www-form-urlencoded(デフォルト)は、HTMLフォームで標準的に使われるコンテンツタイプまたはエンコード方式です。ただし、このフォームを使ってファイルを送信する場合は、multipart/form-data方式でエンコードする必要があります。multipart/form-dataは、HTMLフォームに含まれる複数のパーツやフィールドを表すために使われます。各パーツは、リクエストボディ内で名前と値のペアとして送信されます。それぞれの名前と値は、入力フィールドのname属性と値に対応します。text/plainは、書式設定のないテキストデータを送信するために使われ、ファイルの送信には使用できません。
ファイルをアプリケーションサーバーに送信する目的は、通常、サーバーでファイルを処理する、保存する、またはその両方です。サーバーアプリケーションでは、アップロードされたファイルを次のような方法で処理・保存できます。
独自サーバーのファイルシステムに保存する方法です。アプリケーションが実行されているサーバー上の専用ディレクトリや、ファイル保存専用の別サーバーを利用できます。
データベースに保存する方法です。
Amazon S3やCloudinaryなどのクラウドストレージにファイルをアップロードする方法です。
このチュートリアルでは、最初の方法でファイルアップロードを処理します。
Fastifyでファイルアップロードを実装する
このチュートリアルを始める前に、次のものを用意してください。
Node.jsがインストールされていること
Visual Studio Code(VS Code)など、お好みのコードエディター
Postman。サインインしてWebアプリを使うか、コンピューターにインストールしてください。
以降のセクションでは、Fastifyプロジェクトを作成し、ファイルアップロード用のプラグインを設定します。また、複数ファイルのアップロードを処理できる/uploadルートを作成し、そのルートのテストケースを記述します。
プロジェクトを作成してFastifyを設定する
まず、プロジェクトを作成してFastifyを設定します。プロジェクトを作成するディレクトリに移動し、fastify-file-uploadというフォルダーを作成します。ターミナルで次のコマンドを実行してください。
次のコマンドで、このフォルダーに移動します。
次に、お好みのコードエディターでフォルダーを開きます。VS Codeを使用している場合は、次のコマンドを実行できます。
次のコマンドを実行して、プロジェクトを初期化し、package.jsonファイルを作成します。
次に、このプロジェクトで使用する依存関係をインストールします。
そして、app.jsとserver.jsの2つのファイルを作成します。
app.jsにはアプリケーションのコードを記述します。ファイルを開き、次のコードをコピーして貼り付けます。
Fastifyをfastifyからインポートする
このコードでは、Fastifyフレームワークをインポートし、そこからアプリのインスタンスを作成します。次に、server.jsファイルに以下のコードを記述します。
このコードは、アプリケーションの実行時にアプリを提供します。提供中に問題が発生した場合は、エラーを返します。
このプロジェクトではES6モジュール構文を使用するため、JavaScriptファイルをモジュールとして読み込む必要があります。JavaScriptファイルがモジュールとして読み込まれるように、package.jsonにtypeプロパティを追加し、値をmoduleに設定します。
ファイルアップロード用プラグインを設定する
Fastify-multipartは、Fastifyでmultipartコンテンツタイプをサポートします。依存関係のインストール時にプラグインもすでにインストールしたので、次は設定を行います。multipartを'@fastify/multipart'からインポートして登録してください。
app.jsの内容に、+で始まる行を追加します。
ここでは、先ほど作成したFastifyアプリにmultipartを登録しました。これで、アプリケーションのインスタンスがmultipartコンテンツタイプを簡単に解析できるようになります。
ファイルをアップロードする
POSTリクエストからファイルにアクセスする方法はいくつかあります。たとえば、リクエストに含まれるファイルが1つだけだと分かっている場合は、次のコードでアクセスできます。
このコードのrequestは、アップロードルートのハンドラー関数に渡されるリクエストパラメーターです。ハンドラー関数は次のような形になります。
複数のファイルを想定している場合は、file()の代わりにfiles()メソッドを呼び出します。次のようになります。
app.jsファイルに、複数ファイルのアップロードを完全に実装するコードを記述します。その前に、アプリケーションにアップロードされたファイルを保存するディレクトリを作成します。プロジェクトのルートにuploadsという名前で作成してください。
その後、app.jsファイルを次のように変更します。
変更後のコードでは、Node.jsのコアモジュールfs、util、streamから関数をインポートしています。続いて、/uploadルートとそのハンドラー関数を作成しています。
ハンドラー関数内ではreq.files()を使って、リクエスト内のすべてのファイルにアクセスしています。ファイルを1つずつループし、各ファイルについてawait pump(part.file, fs.createWriteStream(`./uploads/${part.filename}`))でアップロードし、先ほど作成したuploadsディレクトリに保存します。
アップロードするファイルは、保存前にメモリへ蓄積されるわけではない点に注意してください。代わりに、受信側でfs.createWriteStream関数を使ってストリームを作成し、part.fileから届くファイルストリームを新しく作成したストリームにパイプします。これにより、ファイルの内容をまずメモリにバッファリングするのではなく、データをチャンク単位で直接ファイルに書き込み、ストリームから届くたびに保存できます。大きなファイルをアップロードする場合に特に便利です。イベントループをブロックせず、アプリケーションでファイルを効率よくメモリ処理できます。
次はサーバーを起動し、Postmanでファイルを送信します。package.jsonファイルのstartスクリプトを次のように置き換えてください。
次のコマンドでサーバーを起動します。
Postmanでhttp://localhost:8000/uploadへのPOSTリクエストを作成します。リクエストのbodyでform-dataを選択し、お好みのファイルを2つアップロードします。

ここでは、article.txtという名前のファイルとcover.jpgという名前のファイルをアップロードしています(それぞれのキーは「article」と「coverImage」です)。アップロード後、ステータスコード200と、戻り値オブジェクト{message : 'files uploaded' }が返されます。

先ほどアップロード用に作成した「upload」ディレクトリにも、ファイルが追加されていることを確認できます。
これで、fastify/multipartプラグインを使った複数ファイルのアップロード処理ができました。多くのフォームでは、ファイルだけでなく、ファイルとその他のテキストフィールドの両方を指定する必要があります。このようなフォームを処理するには、ファイルストリームとその他のフィールドの両方を扱えるよう、既存のコードを変更します。
そのために、app.jsファイルを次のように変更します。
今回はreq.files()ではなくreq.parts()を使い、ファイルストリームとフィールドの両方にアクセスしています。また、ループ内ではif文で各パーツがファイルかどうかを判定し、フィールドとは異なる方法で処理しています。
次は、もう一度リクエストを送信します。ファイルを保存し、npm startでサーバーを停止してから再起動してください。Postmanに戻り、前のリクエストのキーと値のペアを削除して、新しいものを作成します。最初のフィールドには氏名を入力し、その後、以前と同じようにファイルを2つ指定します。
注:同じファイルを使用する場合は、前回アップロードしたファイルをuploadsディレクトリから削除してください。
リクエストは次のようになります。

このように設定できていれば、アプリケーションで複数のファイルストリームとフィールドを処理できます。
テストケースを作成する
ファイルアップロードの実装を保護するには、テストを書く必要があります。では、Fastifyでファイルアップロードのテストケースを書くにはどうすればよいでしょうか。このチュートリアルでは、テストフレームワークのtap、パッケージのform-auto-content、Fastifyの.injectメソッドを使ってテストケースを作成します。
プロジェクトのルートにapp.test.jsファイルを作成します。次のコードをコピーして貼り付けてください。
ファイルパス./sample_files/file.txtと./sample_files/cover.jpgを、ファイルシステム上にある任意の2つのファイルへのパスに置き換えます。または、こちらからこれらのファイルをコピーできます。
Fastifyの.injectメソッドを使うと、実際のサーバー上でコードを実行せずに、偽のHTTPリクエストをアプリケーションに送信できます。先ほどのコードでは、formAutoContentを使ってmyFormというフォームを作成し、テストケース内で偽のPOSTリクエストのリクエストボディとして使っています。テストケースは1つだけで、複数のファイルを含むフォームを送信したときに、アプリケーションがstatusCodeとして200を返すことを確認します。
このテストを実行し、成功することを確認します。package.jsonファイルのtestスクリプトを次のように置き換えてください。
次に、以下を実行します。
テストが成功し、次のような出力が表示されるはずです。

このチュートリアルのコードは、こちらのGitHubリポジトリで入手できます。
まとめ
この記事では、ファイルアップロードの基礎と、ファイルを含むフォームがmultipart/form-data content-typeでのみエンコードされる理由を学びました。また、サーバーアプリケーションでファイルを処理・保存する方法として、同じサーバー上の専用ディレクトリに保存する方法と、Amazon S3などのクラウドストレージにアップロードする方法があることも学びました。さらに、Node.js向けの高速でオーバーヘッドの少ないWebフレームワーク、Fastifyでアップロードを処理する方法を紹介しました。
さらに詳しく知りたい方は、Fastify-multipartプラグインのドキュメントとFastifyのテストガイドをご覧ください。



