概要
JavaScriptでブラウザのメモリの内容からテキストファイルを生成してダウンロードさせます。
次の2つの手順に分けます。
- URLを作る
- ダウンロードする
URLを作る
ダウンロード用のURLを作る方法は2つあります。
data URIs
data URIsでコンテンツタイプとコンテンツを指定したURLスキームを作ります。
例えば:
data:,hello
です。
エスケープ
直接文字列を指定した場合、改行が無視されます。整形したJSONファイルを作る際はencodeURIComponentを使ってエンコードします。
例えば:
`data:,${encodeURIComponent(JSON.stringify(data, null, 2))}`
Blob URLs(HTML5)
BlobオブジェクトとURL.createObjectURLを使ってURLを作ります。
例えば:
URL.createObjectURL(new Blob(['hello'], {
type: "text/plain"
})
まとめ
生成する文字列が短い場合はdata URIs
を使いましょう。
長文をurlエンコードすると、サイズが大きくなることがあります。Blob URLsを使いましょう。
ダウンロードする
window.location
location.href
に上で作成したURLを代入するとダウンロードが開始されます。
data URIs
例えば
location.href = 'data:text/plain;charset=UTF-8,hello'
です。
GoogleChromeでの制約
GoogleChrome(Version 60.0.3112.101)で、window.locationを使ってダウンロードしようとすると以下のエラーが出ます。
Not allowed to navigate top frame to data URL: data:text/plain;charset=UTF-8,hello
これを回避するには
Intent to Deprecate and Remove: Top-frame navigations to data URLs - Google グループ によると
- non-browser-handled MIME types
- download属性
のいずれかを使います。
non-browser-handled MIME types
を使う場合は、MIME typeにapplication/octet-stream
を指定します。
例えば
location.href = 'data:application/octet-stream, hello'
です。この場合でも
Resource interpreted as Document but transferred with MIME type application/octet-stream: "data:application/octet-stream, hello".
とWarningが表示されます。
download属性を使う方法は後述します。
FirefoxとSafariでの制約
Firefox(54.0.2), Safari(Version 10.1.1 (12603.2.4))では、MIME typeがdefault(text/plan)の場合、ダウンロードされずに遷移します。
MIME typeにapplication/octet-stream
を指定すれば、ダウンロードが開始されます。
blob URLs
例えば:
location.href = URL.createObjectURL(new Blob(['hello'], {
type: "text/plain"
}))
GoogleChromeでの制約
GoogleChrome(Version 60.0.3112.101)では、MIME typeがdefault(text/plan)の場合、ダウンロードされずに遷移します。
MIME typeにapplication/octet-stream
を指定すれば、ダウンロードが開始されます。
location.href = URL.createObjectURL(new Blob(['hello'], {
type: "application/octet-stream"
}))
この場合でも
Resource interpreted as Document but transferred with MIME type application/octet-stream: "blob:null/30e18e5d-120f-41e5-837b-b4f015e94ae0".
とWarningが表示されます。
FirefoxとSafariでの制約
Firefox(54.0.2), Safari(Version 10.1.1 (12603.2.4))では、MIME typeがtext/planの場合、ダウンロードされずに遷移します。
MIME typeにapplication/octet-stream
を指定すれば、ダウンロードが開始されます。
aタグ download属性(HTML5)
HTML5ではaタグにdownload属性が追加されました。
download属性を指定すると
- ページ遷移がファイルダウンロードに
- ダウンロードするファイル名を指定できる
例えば:
<a id="link1" download="hello.txt" href="data:,hello">Data Scheme URL</a>
動的に作成したファイルを設定する場合は、aタグのclickイベントハンドラーでhref
属性を書き換えます。
例えば:
document
.querySelector('a')
.addEventListener('click', (e) => e.target.href = `data:application/json;charset=UTF-8,${JSON.stringify({massage:"hello"})}`)
Blob Scheme URLを使うこともできます。例えば:
document
.querySelector('a')
.addEventListener('click', (e) => e.target.href = URL.createObjectURL(new Blob(['hello'], {
type: "text/plain"
})))
リンククリック時のブラウザの遷移動作はイベントハンドラーの後に実行されます。
まとめ
- マルチブラウザ対応
- ファイル名を指定したい
ならば、download属性を使いましょう。
サンプルコード
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>text file download</title>
</head>
<body>
<article>
<h1>location.href</h1>
<button id="button1">data URIs(With default MIME type)</button>
<button id="button2">data URIs(With MIME type application/octet-stream)</button>
<button id="button3">Blob URLs(text/plain)</button>
<button id="button4">Blob URLs(application/octet-stream)</button>
</article>
<article>
<h1>a tag download attribute</h1>
<a id="link1" download="hello.txt" href="data:,hello">data URIs(text)</a>
<a id="link2" download="hello.json" href="data:,hello">data URIs(JSON)</a>
<a id="link3" download="hello.txt" href="#">Blob URLs(text)</a>
<a id="link4" download="hello.json" href="#">Blob URLs(JSON)</a>
</article>
<script type="text/javascript">
const data = {
massage: "hello",
detail: {
count: 1,
isError: false,
content: 'ワールド'
}
}
document.querySelector('#button1').addEventListener('click', () => location.href = 'data:,hello')
document.querySelector('#button2').addEventListener('click', () => location.href = 'data:application/octet-stream, hello')
document.querySelector('#button3').addEventListener('click', () => location.href = URL.createObjectURL(new Blob(['hello'], {
type: "text/plain"
})))
document.querySelector('#button4').addEventListener('click', () => location.href = URL.createObjectURL(new Blob(['hello'], {
type: "application/octet-stream"
})))
document.querySelector('#link2').addEventListener('click', (e) => {
e.target.href = `data:,${encodeURIComponent(JSON.stringify(data, null, 2))}`
})
document.querySelector('#link3').addEventListener('click', (e) => e.target.href = URL.createObjectURL(new Blob(['hello'], {
type: "text/plain"
})))
document.querySelector('#link4').addEventListener('click', (e) => e.target.href = URL.createObjectURL(new Blob([JSON.stringify(data, null, 2)], {
type: "text/plain"
})))
</script>
</body>
</html>