非同期通信でZipファイルをダウンロードさせる方法

非同期通信でZipファイルをダウンロードさせる方法:


背景

axiosでS3からzipをダウンロードして、それをダウンロードさせる場面があったので、良くある感じでコードを書いてみた。

前提として 

  • S3にはバックエンドから発行されるPresigned URLを使う
  • バックエンドを通さず、そのURLに直接アクセスする


最初に書いたコード

await { data } = axios.get('....') 
const blob = new Blob([data], { type: 'application/zip' }) 
 
const uri = URL.createObjectURL(blob) 
const link = document.createElement('a') 
link.download = 'sample.zip' 
link.href = uri 
link.click() 


その結果…

ファイルのダウンロードは成功するけれど、解凍するとZipの中身が壊れている…

怪しいと思い、拡張子を .txt にして開いてみると、バイナリのようなテキストが並んでるので、Zipとして正しく解釈できていない模様。

フロントエンドだけで完結する対策として、この2つを思いついたので、どっちが良いか試してみる

  1. S3 Presigned URL に直接アクセスさせる
  2. ファイルの変換をうまくやる


動作確認


1. S3 Presigned URLに直接アクセスさせる

const link = document.createElement('a') 
link.download = 'sample.zip' 
link.href = '....' 
link.click() 
とりあえずダウンロードはできる。けれど、ファイル名が download で指定したものとは違う…

ググった結果ここにたどり着く。

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-download

This attribute only works for same-origin URLs.
という訳で、直接S3 Presigned URLにアクセスするとファイル名を制御できない。

セキュリティ考えるとまあそうかなという仕様ですね。


2. ファイルの変換をうまくやる

https://developer.mozilla.org/ja/docs/XMLHttpRequest/Sending_and_Receiving_Binary_Data

ArrayBufferを指定して、明示的にバイナリデータを受信するように設定、

const { data } = await axios.get('....', { 
  responseType: 'arraybuffer', 
  headers: { Accept: 'application/zip' }, 
}) 
const blob = new Blob([data], { type: 'application/zip' }) 
または、直接Blobを指定して構築してもらう。

const { blob: data } = await axios.get('....', { 
  responseType: 'blob', 
  headers: { Accept: 'application/zip' }, 
}) 
これであとは、ファイルへのリンクをクリックさせるだけ!

const uri = URL.createObjectURL(blob) 
const link = document.createElement('a') 
link.download = 'sample.zip' 
link.href = uri 
link.click() 
これで無事、Zipファイルがダウンロードできました。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2020-12-01 09:41:49 RSSフィード2020-12-01 09:00 分まとめ(69件)