Express.jsで画像などのファイルをダウンロードする方法です。
本記事の内容は以下のドキュメントに書かれている内容の説明になります。
https://expressjs.com/ja/api.html
実行環境
express.js 4.17.1
MacOS 10.15.7
方法その1 簡易的な方法
importexpressfrom'express';exportconstrouter:express.Router=express.Router()router.get('/download',(req,res)=>{res.download('images/どね.jpg')})downloadメソッドを使います。ファイルパスを引数に取ることになるので、一度ファイルをストレージのどこかに置く必要があります。すでにBuffer形式になっている場合は少し面倒ですね。
方法その2 headerに指定
importexpressfrom'express';importfsfrom'fs/promises'exportconstrouter:express.Router=express.Router()router.get('/set',async(req,res)=>{constimg=awaitfs.readFile('images/どね.jpg')constfileName=encodeURIComponent('どね.jpg')res.set({'Content-Disposition':`attachment; filename=${fileName}`})res.status(200).send(img)})レスポンスのヘッダーを直接指定します。Content-Dispositionのattachmentを指定することでブラウザ側でダウンロードファイルであることを認識してくれます。
それ以外にも以下の処理が必要になります。
- Bufferファイルを一度読み込んでbodyに入れる
- 日本語ファイル名の可能性がある場合はファイル名にエンコードをかける
filenameとfilename*の違いについて
引数の filename と filename* の違いは、 filename* が RFC 5987 で定義されているエンコーディングを使用するという点のみです。単一のヘッダーフィールドの値に filename と filename* の両方が存在する場合は、両方が解釈できる場合、 filename* が filename よりも優先されます。
方法1ではfilename*が自動で使われて、方法2ではどちらも指定できます。
| 方法1 | 方法2 |
|---|---|
![]() | ![]() |
いくつかのブラウザで試しましたがすべてfilename*を認識できていました。使える文字もこちらのほうが多いようです。あえてfilenameを使う必要はなさそうです。もし使えないブラウザがあったら教えていただきたいです。
試したブラウザを以下に置いておきます。
| ブラウザ | バージョン | 対応状況 |
|---|---|---|
| Chrome | 88 | ○ |
| FireFox | 88 | ○ |
| Microsoft Edge | 88 | ○ |
| Internet Explorer | 11 | ○ |
まとめ
たいていはフレームワークに乗っかった方が後々困ることもないので方法1推奨です。
修正がかかったらフレームワーク側を直せば良い話ですし。

