2016/12/22

【JavaScript】Safariでファイルを強制ダウンロードさせようとしてハマった

フロントエンドを生業としていると、クロスブラウザ対応という悪魔の作業がまっている。現在主に使われているブラウザは、Internet Explorer、MS Edge、Chrome、Firefox、Safariが挙げられる。(Opera? そんなブラウザは知らない)

これらのブラウザでファイルのダウンロードをさせようとすると、Safariでつまずく。IEですらできるのに、Safariだとできない。新しいタブにダウンロードするファイルの内容を表示して、ユーザにコピペさせるという最悪の動作しかできない。そのようにクライアントサイド(JavaScript)だけでファイルをダウンロードさせる方法を試しているうちにSafari沼にハマってしまった。


ということで、試した内容をまとめようと思う。
  1. aタグにdownload属性を追加する
  2. Content-Typeにapplication/octet-streamを指定する
  3. Content-Dispositionを指定する(サーバサイドの実装が必要)

検証環境は以下のとおり。
  • macOS Yosemite
  • Safari 10.0.2


aタグにdownload属性を追加する(失敗1)


まずは、HTML5で新たに追加されたdownload属性を追加する方法。
この属性を追加すると、リンクをクリックしたときにダウンロードするように促したりできる。
const blob = new Blob(['hoge'], { type: 'text/plain' });
const a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = 'dl1.txt';
a.target = '_blank';

a.click();

より具体的でかつクロスブラウザ対応したい場合は、以下の記事を参考にしてほしい。
実際に実行してみていただければわかるが、Safariではタブにファイルの内容を表示するだけに留まってしまう。(今回はa.target = '_blank'を指定しているので別タブで開かれる)



Content-Typeにapplication/octet-streamを指定する(失敗2)


次に、blobのContent-Typeにapplication/octet-streamを指定する方法。
このMIMEタイプを使うと、headerにContent-Disposition: attachementを指定したのと同様の動作になり、ブラウザに「名前を付けて保存」をさせようとする。
const blob = new Blob(['hoge'], { type: 'application/octet-stream' });
saveAs(blob, 'dl2.txt')

クロスブラウザ対応が面倒でFileSaver.jsを使っている(saveAs)。処理的には先ほど紹介したdocument.createElement('a')a.click() と同じような動きになる。

実際に実行してみると、なんと自動的にファイルがダンロードされたではありませんか!!
ただ、ちょっとした問題が…。

それは、「Failed to load resource: フレームの読み込みが中断しました。」というエラーがコンソールに出力されていること。
もうひとつが、ファイル名が「Unknown」としてダウンロードされてしまうこと。

いろいろ調べても解決方法が見つからず、泣く泣くこの実装は断念した。



Content-Dispositionを指定する(成功)


会社のSlackで質問してみたり、友人に質問してみたり、stackoverflowをみたり、、、行き着いたのは、サーバサイド開発だった。headerにContent-Dispositionを指定することで、ローカルに保存させるように動作する。
<?php
header('Content-Type: text/plain');
header('Content-Disposition: attachment; filename=' . $_GET['id']);

echo json_encode('いろいろ');
?>
location.href = example.com/download?id=1234

ようやくSafariでも強制ダウンロードさせることができた。



さいごに



クライアントサイドで実装できる2つは、具体的には以下のような動作になる。


巷では「IE○ね!」とよく聞くが、私は断然「Safari○ね!!」

IE6〜9の対応をしたことがないからかもしれないけど、最近のIEはわりとまともに動いてくれる。Edgeなんて最高じゃないか!
それにくらべSafariはひどい。ジョブズの息吹だかしらないが、Safariがなくなれば世界もちょっとは平和になるだろう。少なくとも私の仕事人生において無駄な時間を費やさなくてすむ。

そして世の中のすべてのブラウザがChromeに統一されますように。



参考サイト




written by @bc_rikko

0 件のコメント :

コメントを投稿