スポンサーリンク

JavaScript/HTML5で,SJISの日本語CSVファイルをWebページ上で動的に生成してダウンロードするサンプルコードと解説 (動作デモ付き。Excelで開いても文字化けしない)

HTML5のJavaScriptで,Webページ内でCSVファイルを動的に生成してダウンロードする。

CSVファイルの中身は日本語で,文字コードはSJIS(Shift-JIS)とする。

サーバ側の処理は一切不要で,JavaScriptだけで実装する。


下記URLに,実際に動く動作デモがあります。

html5のFile APIで2バイト文字のCSVを出力するサンプル
http://computer-technology.study-tips...

※HTML5に対応したブラウザで開いて下さい。


以下はソースコード。

<!doctype html>
<html>
<head>
	<meta charset="utf-8">
	<title>html5のFile APIで2バイト文字のCSVを出力するサンプル</title>
	
	<script src="./encoding.js"></script>
</head>
<body>

<h1>html5のFile APIで2バイト文字のCSVを出力するサンプル</h1>


<input type="button" value="CSVリンク生成" onclick="f()"><br>

<br>

<a id="hoge">ここからCSVをダウンロード</a><br>

<script>


// 文字列から,Unicodeコードポイントの配列を作る
function str_to_unicode_array( str ){
	var arr = [];
	for( var i = 0; i < str.length; i ++ ){
		arr.push( str.charCodeAt( i ) );
	}
	return arr;
};


// CSVダウンロードリンクを生成する
function f(){

	// CSVの内容
	// SJIS変換せずにCSV出力するとExcelでは文字化けする
	var csv_line = "あ,日本語,a,b,c,d,e\r\nSJIS,ならば,文字化け,しない";

	// Unicodeコードポイントの配列に変換する
	var unicode_array = str_to_unicode_array( csv_line );
	
	// SJISコードポイントの配列に変換
	var sjis_code_array = Encoding.convert( 
		unicode_array, // ※文字列を直接渡すのではない点に注意
		'SJIS',  // to
		'UNICODE' // from
	);
	
	// 文字コード配列をTypedArrayに変換する
	var uint8_array = new Uint8Array( sjis_code_array );
	
	// 指定されたデータを保持するBlobを作成する
	var blob = new Blob([ uint8_array ], { type: 'text/csv' });

	// Aタグのhref属性にBlobオブジェクトを設定し、リンクを生成
	window.URL = window.URL || window.webkitURL;
	document.getElementById("hoge").href = window.URL.createObjectURL(blob);
	document.getElementById("hoge").download = "test.csv";
	
}


</script>

</body>
</html>

SJISへの変換のために,encoding.js というライブラリを使っている。

encoding.jsのダウンロード
https://raw.githubusercontent.com/pol...

コードの解説

このコードのキモは,BlobとcreateObjectURLの組み合わせ。

こうすれば,サーバ上に存在しないURLを動的に生成し,バイナリをダウンロードできる。


このような仮想的なURLのことを,Blob URLという。

HTML5のFile APIでよく使う便利な機能だ。

File APIs(Blob, BlobURL, ArrayBuffer, FileReader) - Qiita
http://qiita.com/TypoScript/items/0d5...

  • BlobURL は "blob:..." から始まる仮想的なURL
    • URL.createObjectURL(source:Blob):BlobURLString で動的に生成する
    • URL.createObjectURL は常にユニークな BlobURL を生成する
  • 生成したBlobURLはブラウザか終了するまで有効


HTML5の「Web Workers API」を,別ファイルを使わずページ単体で利用するサンプル (createObjectURLがあれば,1ファイルでマルチスレッドのJSコーディングが可能)
http://language-and-engineering.hatenablog.jp/entry/20140331/HTMLfiveWebWorke...

// JSコードをBLOBオブジェクトに変換
var b = new Blob( [ js_code ], {type : "text/javascript"} );

// BLOBオブジェクトをURLに変換
var worker_url = window.URL.createObjectURL( b );

// URLを使ってWeb Workerを生成
worker = new Worker( worker_url );


また,SJISへの文字コード変換部分のかなめは,encoding.js の convert() の部分。

ここは文字列を渡すのではなく,文字コードの配列を渡すという仕様だ。

JavaScriptで文字コード変換ライブラリ作ってみた | 圧縮電子どうのこうの
http://polygon-planet-log.blogspot.jp...

  • 文字コード変換ライブラリを JavaScript で作りました。
  • 文字列ではなく配列 or TypedArray で処理します。


javascriptで文字コード変換 - Qiita
http://qiita.com/weal/items/3b3ddfb81...

  • encoding.jsの文字コード変換は, 配列を渡し配列で返る
  • なので,文字列から配列に変換する関数は別途用意する必要がある。


[Firefox OS] Privileged App にて,XMLHttpRequest で得た Shift_JIS を UTF-8 に変換する. | hirooka.pro
https://hirooka.pro/?p=1765
var uInt8Array = new Uint8Array(req.response);
var utf8Array = Encoding.convert(uInt8Array, 'UTF8', 'SJIS');


encoding.js/README_ja.md at master · polygonplanet/encoding.js · GitHub
https://github.com/polygonplanet/enco...
// UTF-8のデータをShift_JISに変換
var utf8Array = new Uint8Array(...) or [...] or Array(...) or Buffer(...);
var sjisArray = Encoding.convert(utf8Array, 'SJIS', 'UTF8');


このため,文字列を charCodeAt() で文字コードの数値に変換し
encoding.jsに配列として渡している。

String.prototype.charCodeAt() - JavaScript | MDN
https://developer.mozilla.org/ja/docs...

  • 文字の Unicode コードポイントを10進数値で返します。


encoding.jsによる文字コード変換がすんだら,次はBlobの生成だ。


Blobを生成するには,バイナリデータを1バイトずつ区切った配列が必要だ。

「1バイトずつ」というのは, つまり8ビットなので,型でいうとUint8。

その配列は Uint8Array というオブジェクトで,「型つき配列」の一種。


なので,SJISの文字データを Uint8Array にして,さらにそれを Blob のコンストラクタに渡せばよい。

十二章第五回 File API — JavaScript初級者から中級者になろう — uhyohyo.net
http://uhyohyo.net/javascript/12_5.html

  • ArrayBufferというのは要するに「バイナリデータが入っているオブジェクト」ということ
    • ArrayBufferの特徴は、実際にメモリ上に連続する領域が確保されているということ
  • ファイルをBlobとして得られた段階ではまだそのファイルをメモリ上に読み込んでおらず、それを実際にメモリ上に(文字列やArrayBufferの形で)読み込むのがFileReader。
  • そして、ArrayBufferを読み込むために使うのが「型つき配列」
    • 型つき配列の中でも,よく使うのがUint8Array
    • Uint8とは、「8ビットで符号なし整数」ということです。Cでいう「unsigned char」にあたるもの
    • 8ビットということはすなわち1バイトですから、バッファを1バイトずつに区切って配列にしたもの


Uint8Array - JavaScript | MDN
https://developer.mozilla.org/ja/docs...

  • Uint8Array タイプは、8 ビット符号なし整数値の配列を表します。


JavaScriptでバイナリファイルを扱う方法 | Tech-Sketch
http://tech-sketch.jp/2013/01/javascr...

  • ArrayBufferView のサブクラスである Uint8Array とMimeTypeを渡すことでBlobオブジェクトを作成します。


HTML5のJavaScriptでバイナリファイルを扱う(その2) - NullPointer's Blog
http://paulownia.hatenablog.com/entry...

  • Uint8Arrayは、ArrayBufferのデータに符号あり8ビット整数としてアクセスするためのラッパー. 普通に配列のようにアクセスできる


これで Blob が生まれるので,createObjectURL して DOM上にセットすれば完了。

これだけでもダウンロードは可能だが,
ついでにダウンロード時のファイル名を
aタグのdownload属性に指定する。

これもHTML5以降の機能だ。

download属性 ≪ a要素 ≪ メタデータ ≪ 要素 ≪ HTML5入門
http://html5.cyberlab.info/elements/t...

  • download属性には、ダウンロード時のファイル名を指定できる。


以上で全処理が完了となる。


ちなみに,日本語CSVを文字化けさせずに生成するためには,SJISにする必要はない。

UTF-8出力で先頭にBOMを付けてやれば,SJIS変換せずにCSVを作れる。

そのBOMの部分も,やはり Uint8Array で作成する。

ブラウザでCSVダウンロードサンプル | javascript | CACOM
http://www.cacom.jp/se/javascript/%E3...
var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);


ブラウザだけで Excel 向け日本語 CSV ファイルを作る方法 - do_akiの徒然想記
http://d.hatena.ne.jp/do_aki/20130225...
var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);


encodeURIComponent で作る手もある。

JavaScript - ブラウザからjsの配列をcsvとしてダウンロードする。 - Qiita
http://qiita.com/ooooooo_q/items/69d6...

  • 'data:text/csv,' + encodeURIComponent(str);


とはいえ,どうしても内部的にSJISにしたいケースもあるから,
今回のサンプルのように encoding.js を使えるようにしておくと便利。

集計したデータをCSVでダウンロードするには? – cybozu.com developer network
https://cybozudev.zendesk.com/hc/ja/a...

  • UTF-8で出力したCSVファイルをそのままではExcelで開けない
    • なので、ファイルの先頭にBOM(Byte Order Mark)を付けることで、Excelで開けるようにしています。
  • もしどうしてもShift-JISで出力したい場合は、外部ライブラリを利用することでエンコーディングの変換を行うことができます。

補足

この記事は,下記の質問への回答として執筆しました。

html5-FileAPIで2バイト文字CSV出力 下記の様にHTML5-FileAPIで…
http://q.hatena.ne.jp/1436496334