これもコピィ・ライター作成時に、
- 動的に生成した画像をボタンをクリックしてダウンロード
する機能を実現する過程で、HTML5・A要素(リンク)の download 属性について調べたことに関する覚え書き。
HTML5・A要素(リンク)の download 属性とは
download
HTML5
a 要素 - HTML | MDN
この属性は、ユーザがリンクをクリックするとリソースをローカルファイルとして保存することを促されるように、リソースをダウンロードするために使用されるハイパーリンクであることをページ作者が意図して記述します。属性に値が指定された場合、ユーザがリンクをクリックしたときに開く保存プロンプトの、デフォルトのファイル名として解釈します (もちろん、ユーザは実際にファイルを保存する前にファイル名を変更できます)。使用可能な値に制限はありません (ただし/
および\
はアンダースコアに変換して、特定のパスヒントを防ぎます) が、多くのファイルシステムには、ファイル名に使用できない文字があることを考慮する必要があります。ブラウザがファイル名を調整するかもしれません。
補足:
- この属性は、ユーザが簡単に JavaScript を使用するプログラムで生成されたコンテンツ (例えばオンラインのお絵かき Web アプリを使用して描いた画像) をダウンロードするため、
blob:
URL およびdata:
URL とともに使用できます。- この属性で指定したものと異なるファイル名を
Content-Disposition:
HTTP ヘッダで与えている場合は、この属性より HTTP ヘッダが優先します。- この属性を指定するとともに
Content-Disposition:
でinline
を指定している場合、Firefox はファイル名と同様にContent-Disposition
を優先しますが、Chrome はdownload
属性を優先します。- Firefox 20 では、この属性は同一生成元のリソースへのリンクにのみ受け入れられます。
とりあえず、Can I use... Support tables for HTML5, CSS3, etcで調べてみると、最新のFirefox・Chrome・Operaはサポートしている模様。
またIEは無いのか……と思ったが、代替手段(window.navigator.msSaveOrOpenBlob())でなんとかなりそうではあった。
Safari? えっと、知らない子ですね……。
実験とブラウザ毎の結果
- [A] 画像ファイルを Data URL に変換したもの
- [B] 画像ファイル(サイト内)へのリンク
- [C] 画像ファイル(外部サイト)へのリンク
の三種類の A(リンク)要素を用意し、それぞれに download 属性でファイル名を指定した場合のテストを行った。
IE用には別途、スクリプトで window.navigator.msSaveOrOpenBlob() を使った細工をしてある。
それぞれのリンクをクリック(タップ)した結果、以下のようになった。
Google Chrome 43.0.2357.134 m |
Firefox 39.0 |
Opera 30.0.1835.125 |
IE11 | Chrome for Android 43.0.2357.93 |
|
[A] | ○ | ○ | ○ | ▲ | ○ |
[B] | ○ | ○ | ○ | ▲ | × |
[C] | △ | × | △ | ※ | × |
- ○:download属性で指定したファイル名でダウンロード
- △:download属性は無視され、href属性を元にしたファイル名でダウンロード
- ▲:download属性で指定したファイル名でダウンロード
IE用に、リンクに onclick トリガを設定し、window.navigator.msSaveOrOpenBlob() を使った細工を入れている - ※:別窓(タブ)で画像が開く
IE用に、リンクに onclick トリガを設定し、XMLHttpRequest で画像を取得させ、エラーが発生したら別窓(タブ)で開く細工を入れている - ×:hrefで指定された先にページ遷移
外部サイト上のファイルの場合の動作はセキュリティがらみの制限なのだろうと推測されるが、Chrome for Android でサイト内ファイルへのリンクまでページ遷移してしまうのは謎。なぜ PC 版と動作を変える必要があったのか?
関連する処理など
canvas 要素の Data URL への変換
var dataURL = canvas.toDataURL(type); // canvas は HTML5 Canvas の DOM要素、typeは画像の種別('image/png', 'image/jpeg'等)
データの Blob オブジェクトへの変換
var blobObject = new Blob([data], {'type' : mimeType}); // data は元データ、mimeType は元データの MIMEタイプ('image/svg+xml'等)
Data URL の Blob オブジェクトへの変換
function make_blob_from_dataurl(dataurl) { if (!dataurl.match(/^data:(.*?);base64,(.*)$/)) { return null; } var type = RegExp.$1, base64 = RegExp.$2; var bin = atob(base64), bin_length = bin.length; var buffer = new Uint8Array(bin_length); for (var ci=0; ci < bin_length; ci++) { buffer[ci] = bin.charCodeAt(ci); } var blobObject = new Blob([buffer.buffer], {type: type}); return blobObject; }
ファイルを Blob オブジェクトとして取得
var xhrObject = new XMLHttpRequest(); xhrObject.open('GET', url, true); xhrObject.responseType = 'blob'; xhrObject.onload = function(event) { var blobObject = xhrObject.response; // ◆ 以下、blobObject を用いた処理を記述 }; xhrObject.onerror = function(event) { //※(許可されていない)外部サイトのファイルを取得しようとすると以下のようなエラーが発生(IEの例) // SEC7118: (取得しようとしたURL) の XMLHttpRequest には Cross Origin Resource Sharing (CORS) が必要です。 // ファイル: (元ファイル) // SEC7120: 元の http://(元ドメイン) が Access-Control-Allow-Origin ヘッダーに見つかりません。 // ファイル: (元ファイル) // SCRIPT7002: XMLHttpRequest: ネットワーク エラー 0x80070005, アクセスが拒否されました。 window.open(url); // 次善の策として、例えばポップアップで開く }; xhrObject.send();
Blob オブジェクトの Blob URL への変換
var blobURL = (window.URL || window.webkitURL).createObjectURL(blobObject);
Blob オブジェクトをファイルとして保存したり開いたりするイベントの発生(IE10+)
window.navigator.msSaveOrOpenBlob(blobObject, filename);
その他気付いたことなど
- A要素(リンク)の場合、jQuery によってクリックイベントを発生( .click() や .trigger('click'))させても、ダウンロードやページ遷移は行われない。
これらを発生させたい場合には、MouseEvent を作成するか、DOM の .click() をコールする。