ブラウザでBase64で受け取ったファイルをダウンロードする

受託開発担当のRyuです。

先日、サーバからBase64で受け取ったPDFをそのままダウンロードしたい場面に出くわしました。
難なく出来るだろうと高をくくっていたのですが、つまづいたのでメモとして残します。

簡単に流れを説明すると、Base64をBlobへと変換し、それをBlob URL Schemeとしてリンクタグに動的に埋め込み、強制的にそれを叩きます。

<script type="text/javascript" src="jquery-3.1.1.min.js"></script>
<script type="text/javascript">
	$(function() {
		$ajax(~中略~)
		.done(function(data, type) {
			downloadPdf(data);
		});
	});

	/**
	 * Base64とMIMEコンテンツタイプからBlobオブジェクトを作成する。
	 * 
	 * @param base64
	 */
	function downloadPdf (base64) {
		var mime_ctype = "application/pdf";
		var blob = toBlob(data, mime_ctype);

		if (window.navigator.msSaveBlob) {
			// IEやEdgeの場合、Blob URL Schemeへと変換しなくともダウンロードできる
			window.navigator.msSaveOrOpenBlob(blob, "file.pdf"); 
		} else {
			// BlobをBlob URL Schemeへ変換してリンクタグへ埋め込む
			$("#file_dl").prop("href", window.URL.createObjectURL(blob));
			// リンクをクリックする
			document.getElementById("file_dl").click(); 
		}
	}
	
	/**
	 * Base64とMIMEコンテンツタイプからBlobオブジェクトを作成する。
	 * 日本語対応。
	 * 
	 * @param base64 
	 * @param mime_ctype MIMEコンテンツタイプ
	 * @returns Blob
	 */
	function toBlob(base64, mime_ctype) {
		// 日本語の文字化けに対処するためBOMを作成する。
		var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);

		var bin = atob(base64.replace(/^.*,/, ''));
		var buffer = new Uint8Array(bin.length);
		for (var i = 0; i < bin.length; i++) {
			buffer[i] = bin.charCodeAt(i);
		}
		// Blobを作成
		try {
			var blob = new Blob([bom, buffer.buffer], {
				type: mime_ctype,
			});
		} catch (e) {
			return false;
		}
		return blob;
	}
</script>

http://amaraimusi.sakura.ne.jp/note_prg/JavaScript/file_binary.html
http://qiita.com/wadahiro/items/eb50ac6bbe2e18cf8813
この辺りを参考にさせてもらいました。

なお、ダウンロードではなくブラウザで表示させるだけの場合は、Base64のままData URI schemeを使えば可能なようです。