ノラWEB屋 野良人(のらんど)- 個人営業のWEB屋さん

【jQuery】ローカルのCSVを取得・変換してCSVで出力する

2016年11月15日

「Misoca(ミソカ)」で通販サイトの請求書を一括作成・一括発行の記事で書いたものです。通販サイトASPからダウンロードしたCSVをMisocaの請求書一括作成のフォーマットに変換するものを、jsで書きました。

CSVを読み込む

ベースはこちらを参考にさせて頂きつつ。

File APIでローカルファイルを読み込む – developers note

エクセルでいじると文字コードがShift-JISになってしまうので、文字コードを変換するライブラリのお世話になります。

JavaScriptで文字コード変換ライブラリ作ってみた | 圧縮電子どうのこうの

サンプルコード

<script src="encoding.js"></script>
<script>
var array;

$(function(){
	$("#sample").change(function(e){
		var file = e.target.files[0];
		// FileReader.onloadイベントにファイル選択時に行いたい処理を書く
		var reader = new FileReader();
		
		// SHift-JIS対応
		reader.readAsBinaryString(file);
		reader.onload = function(e) {
			
			// 文字コード変換
			var result = e.target.result;
			var sjisArray = str2Array(result);
			var uniArray = Encoding.convert(sjisArray, 'UNICODE', 'AUTO'); 
			
			// 文字コード配列を文字列に変換
			var result = Encoding.codeToString(uniArray);
			
			//2次元配列を生成
			array = toArray(result);
			array.splice(result,1);
		}
		
    });

},false);

// CSVテキストを2次元配列にする 
function toArray(csv){
	var result = new Array();
	var rows = csv.split("\n");
	$(rows).each(function(){
		result.push(this.split(","));
	});
	return result;
}

// 文字コード配列に変換
function str2Array(str) {
	var array = [],i,il=str.length;
	for(i=0;i<il;i++) array.push(str.charCodeAt(i));
	return array;
}
</script>
<input type="file" id="sample">

ポイント

サンプルコードでは考慮していませんが、元データに価格が含まれていると、カンマで区切られている場合があります。そのままだと「1,000円」なら「1」と「000」で区切られた状態になるので、結合してやる必要があります。

仮に以下のような販売データがあったとすると…

商品名 価格
商品1 500
商品2 1,000

サンプルコードを使って二次元配列に収めるとこうなります。

var array = [
	["商品名","価格"],
	["商品1",500],
	["商品2","1,000"]
];

これではarray[1][1]が「”1」となり、array[1][2]は「000″」となってしまうので、結合してやる必要があります。

紹介した案件では、価格の箇所が「”」で始まる場合は次の要素と結合、という処理を行いました。

こういう雰囲気でよしなに

for(i = 0; i < array.length - 1; i++ ){
	if( array[i][1].slice(0, 1) == '"' ){
		array[i][1] = array[i][1] + array[i][2];
		array[i].splice(2,1);
	}
}

出力フォーマットに合わせる

配列で取りこんだCSVを、出力したいフォーマットに合わせて変換します。今回はMisocaの請求書一括作成用のフォーマットにつくりかえます。地道にやります。

サンプルコード

<script>
$(function(){
	$('#change').click(function(){
		// 1行目のラベル
		var label = ["請求日","請求番号","件名","請求先名称","敬称","担当者","送付先郵便番号","送付先住所1","送付先住所2","送付先名前1","送付先名前2","送付先名前3","送付先名前4","送付先敬称","消費税設定","お支払い期限","品目1","数量1","単位1","単価1","非課税フラグ1","品目2","数量2","単位2","単価2","非課税フラグ2","品目3","数量3","単位3","単価3","非課税フラグ3","品目4","数量4","単位4","単価4","非課税フラグ4","品目5","数量5","単位5","単価5","非課税フラグ5","品目6","数量6","単位6","単価6","非課税フラグ6","品目7","数量7","単位7","単価7","非課税フラグ7","品目8","数量8","単位8","単価8","非課税フラグ8","品目9","数量9","単位9","単価9","非課税フラグ9","品目10","数量10","単位10","単価10","非課税フラグ10","品目11","数量11","単位11","単価11","非課税フラグ11","品目12","数量12","単位12","単価12","非課税フラグ12","品目13","数量13","単位13","単価13","非課税フラグ13","品目14","数量14","単位14","単価14","非課税フラグ14","品目15","数量15","単位15","単価15","非課税フラグ15","品目16","数量16","単位16","単価16","非課税フラグ16","品目17","数量17","単位17","単価17","非課税フラグ17","品目18","数量18","単位18","単価18","非課税フラグ18","品目19","数量19","単位19","単価19","非課税フラグ19","品目20","数量20","単位20","単価20","非課税フラグ20","取引先管理コード"];
		
		// 出力用配列
		var misoca = [];
		
		// 先頭行
		misoca.push(label); 
		
		for(var i = 1; i < array.length; i++){
			// 1行ずつ処理する
			var row = [];
			
			// 出力したいフォーマットの順番に元データの該当要素をpushしていく
			row.push(array[i][該当箇所]);
			…
			row.push(array[i][該当箇所]);
			
			// 1行分できたら出力用配列へ
			misoca.push(row);
		}
	});
});
</script>
<div id="#change">変換する</div>

案件では、メールアドレスをキーにして同じ人の注文を1行にまとめるみたいな処理をしていますが、地味なのでここでは割愛します。

CSVを書きだす

こちらを参考にさせて頂きました。

javascript で作成したCSVファイルをエクセルで表示可能にする – Qiita

サンプルコード

//配列をカンマ区切り文字列に変換
var csv_string = "";
for (var i=0; i<misoca.length; i++) {<!---->
    csv_string += misoca[i].join(",");
    csv_string += '\n';
}


//ファイル作成
var blob = new Blob([csv_string] , {
    type: "text/csv;charset=utf-8;"
});

//ダウンロード実行

if (window.navigator.msSaveOrOpenBlob) {
    //IEの場合
    navigator.msSaveBlob(blob, 'filename.csv');
} else {
    //IE以外(Chrome, Firefox)
    var downloadLink = $('<a></a>');
    downloadLink.attr('href', window.URL.createObjectURL(blob));
    downloadLink.attr('download', 'filename.csv');
    downloadLink.attr('target', '_blank');

    $('body').append(downloadLink);
    downloadLink[0].click();
    downloadLink.remove();
}

今回は、出力したファイルをエクセルで編集したりせずにそのままMisocaにアップする想定なので、参考サイトにあるBOMの追加や文字コードの変換は省略しています。

まとめ

<script>
var array;

$(function(){
	$("#sample").change(function(e){
		var file = e.target.files[0];
		// FileReader.onloadイベントにファイル選択時に行いたい処理を書く
		var reader = new FileReader();
		
		// SHift-JIS対応
		reader.readAsBinaryString(file);
		reader.onload = function(e) {
			
			// 文字コード変換
			var result = e.target.result;
			var sjisArray = str2Array(result);
			var uniArray = Encoding.convert(sjisArray, 'UNICODE', 'AUTO'); 
			
			// 文字コード配列を文字列に変換
			var result = Encoding.codeToString(uniArray);
			
			//2次元配列を生成
			array = toArray(result);
			array.splice(result,1);
		}
		
    });

},false);

// CSVテキストを2次元配列にする 
function toArray(csv){
	var result = new Array();
	var rows = csv.split("\n");
	$(rows).each(function(){
		result.push(this.split(","));
	});
	return result;
}

// 文字コード配列に変換
function str2Array(str) {
	var array = [],i,il=str.length;
	for(i=0;i<il;i++) array.push(str.charCodeAt(i));
	return array;
}
$(function(){
	$('#change').click(function(){
		// 1行目のラベル
		var label = ["請求日","請求番号","件名","請求先名称","敬称","担当者","送付先郵便番号","送付先住所1","送付先住所2","送付先名前1","送付先名前2","送付先名前3","送付先名前4","送付先敬称","消費税設定","お支払い期限","品目1","数量1","単位1","単価1","非課税フラグ1","品目2","数量2","単位2","単価2","非課税フラグ2","品目3","数量3","単位3","単価3","非課税フラグ3","品目4","数量4","単位4","単価4","非課税フラグ4","品目5","数量5","単位5","単価5","非課税フラグ5","品目6","数量6","単位6","単価6","非課税フラグ6","品目7","数量7","単位7","単価7","非課税フラグ7","品目8","数量8","単位8","単価8","非課税フラグ8","品目9","数量9","単位9","単価9","非課税フラグ9","品目10","数量10","単位10","単価10","非課税フラグ10","品目11","数量11","単位11","単価11","非課税フラグ11","品目12","数量12","単位12","単価12","非課税フラグ12","品目13","数量13","単位13","単価13","非課税フラグ13","品目14","数量14","単位14","単価14","非課税フラグ14","品目15","数量15","単位15","単価15","非課税フラグ15","品目16","数量16","単位16","単価16","非課税フラグ16","品目17","数量17","単位17","単価17","非課税フラグ17","品目18","数量18","単位18","単価18","非課税フラグ18","品目19","数量19","単位19","単価19","非課税フラグ19","品目20","数量20","単位20","単価20","非課税フラグ20","取引先管理コード"];
		
		// 出力用配列
		var misoca = [];
		
		// 先頭行
		misoca.push(label); 
		console.log(array);
		for(var i = 1; i < array.length; i++){
			// 1行ずつ処理する
			var row = [];
			
			// 出力したいフォーマットの順番に元データの該当要素をpushしていく
			row.push(array[i][該当箇所]);
			…
			row.push(array[i][該当箇所]);
			
			// 1行分できたら出力用配列へ
			misoca.push(row);
		}
		
		//配列をカンマ区切り文字列に変換
		var csv_string = "";
		for (var i=0; i<misoca.length; i++) {<!---->
		    csv_string += misoca[i].join(",");
		    csv_string += '\n';
		}
		
		//ファイル作成
		var blob = new Blob([csv_string] , {
		    type: "text/csv;charset=utf-8;"
		});

		//ダウンロード実行

		if (window.navigator.msSaveOrOpenBlob) {
		    //IEの場合
		    navigator.msSaveBlob(blob, 'filename.csv');
		} else {
		    //IE以外(Chrome, Firefox)
		    var downloadLink = $('<a></a>');
		    downloadLink.attr('href', window.URL.createObjectURL(blob));
		    downloadLink.attr('download', 'filename.csv');
		    downloadLink.attr('target', '_blank');

		    $('body').append(downloadLink);
		    downloadLink[0].click();
		    downloadLink.remove();
		}
	});
});
</script>
<input type="file" id="sample">
<div id="change">変換する</div>

なんかもっと美しくなりそうな感じはします。これでもいろいろ割愛してシンプルにしてありますが、実際は顧客データも読み込んで販売データとつき合わせて同じ人の注文を1請求書にまとめたりとか、品目数の上限を超えた場合等々のエラー処理とか色々しているのでさらにゴチャついています。ともあれ、動作の方は問題なく、Misocaのフォーマット通りのcsvを出力できました。

なにより、クライアントが請求書作成にかけていた労力は一気に削減されたと喜んで頂けたのが嬉しいかぎり。

このエントリーをはてなブックマークに追加