DL実装するときに理解すること

DL実装するときに理解すること

Category
Description
この記事はファイルをDLするための実装をAPI(Spring)からフロントまでを説明する記事になります。
Author
Published
Published July 31, 2020
Last Updated
Last Updated July 31, 2020
Writings
この記事は約7分で読めます

はじめに

この記事はファイルをDLするための実装をAPI(Spring)からフロントまでを説明する記事になります。
前提としてAPIとフロントが分かれたアーキテクチャでの実装となります。

データの形式

ファイル自体の形式も様々です。ここではよく使われるファイルの形式をあげていきます。
他にもありますが、有名所として BlobBase64 が使われています。

Blob

Binary Large OBjectの略。データベースへの格納形式でよく使われる。画像や音声、その他のマルチメディアオブジェクトがBLOBとして格納される。

Base64

Base64は、データを64種類の印字可能な英数字のみを用いて、それ以外の文字を扱うことの出来ない通信環境にてマルチバイト文字やバイナリデータを扱うためのエンコード方式。
一番よく聞く形式ではないでしょうか。ブラウザやデバイスによって受け取れない文字列がある場合にBase64にして互換性を保つために使われているそうです。ただし、デメリットはデータ量が約33%増えるため通信量が増えてしまうようです。

APIの実装

API側としてはファイルの形式は決まっているのか、複数ファイルであればZip化するのか、結構キメの問題が多いです。画像DL APIなのか、ZipファイルDL APIなのかで変わってきます。
返す要素が決まっていない場合は Headerをoctet-streamにしてあげればよしなに変えてくれます(あまり推奨はしない)
  • Resourceを返す場合(Spring)
// リソースファイルを読み込み Resource resource = new ResourceLoader().getResource("classpath:" + "/static/image/test.jpg"); return resource;
  • Byte配列で返却する場合(Spring)
Resource resource = new ResourceLoader().getResource("classpath:" + "/static/image/test.jpg"); InputStream image = resource.getInputStream(); // byteへ変換 return IOUtils.toByteArray(image);
  • Zip形式で返却する場合(Spring)
final List<Resource> resources = new ArrayList(); resources.add(new ResourceLoader().getResource("classpath:" + "/static/image/test.jpg")); resources.add(new ResourceLoader().getResource("classpath:" + "/static/image/test2.jpg")); ByteArrayOutputStream data = new ByteArrayOutputStream(); ZipOutputStream zipOut = new ZipOutputStream(data); for (Resource res : resources) { zipOut.putNextEntry(new ZipEntry(res.getFilename())); zipOut.write(IOUtils.toByteArray(res.getInputStream())); } try { zipOut.flush(); } finally { zipOut.close(); data.close(); } return new ByteArrayResource(data.toByteArray());
DL APIの実装で忘れてはいけないのが、content-disposition Headerの設定です。いわゆるファイル名になります。
フロント(JS)からcontent-disposition Headerを読み取れるようにするためにAccess-Control-Expose-Headersを設定する必要があります。
ファイル名をつけて返してあげましょう。
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION); headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename='YYYYMMDD-test.zip'");

Frontの実装

Frontの実装の流れは、APIをコールして、そのレスポンスをダウンロード属性のあるaタグに差し込んで擬似的にクリックしてDLするといった少し面倒な手順なのですが、fileSaverというライブラリを使うことで簡単に実現できます。
以下サンプルコードです。TypeScriptで書かれていますが、なんとなくで読み取っていただけるとw
コード的にはHeaderのcontent-dispositionに含まれている値を正規表現で取り出しファイル名をfile-saverに渡しています。
const getFileNameFromHeader = (content: string = '', defaultName: string = 'download'): string => { const regex = content.match(/filename=["'`](\\S*?)["'`]/); if (isNull(regex)) return defaultName; return regex[1] || defaultName; }; axios.get('/api/download') .then(response => { saveAs(response.data, getFileNameFromHeader(response.headers['content-disposition'], 'download.zip')); })

おわりに

半分備忘録ですが、また実装するときにお世話になると思います。参考にできる部分は参考にどうぞ
それでは

参考URL