一. 需求
最近在项目中遇到一个问题,客户提供的数据是CSV格式的,
需要将CSV文件中的数据转换为SQL语句文件。
😅由于本人不会Excel的vba编程,因此决定使用JS来实现。
二. 实现思路
- 提供一个文件上传框,支持多文件上传,且只能上传csv文件
- 使用
new FileReader()
来读取上传的csv文件 - 每个csv文件都包含标题栏,因此需要从第二行开始读取数据
- 将读取到的数据构造成insert的sql语句然后存放到一个list中
- 创建
Blob
对象,将包含sql的list放入该对象中构建sql文本对象 - 通过
URL.createObjectURL()
获取Blob
对象在内存中的地址 - 创建a标签,配合内存中的地址实现sql文件下载
三. CSV文件例子
"CQ企画番号","共同購入商品コード","CQ商品コード","JANコード","定催区分","部門コード","大分類","中分類","小分類","集品区分","商品名漢字","規格名漢字","入数","納品時容器区分","シール貼付有無区分","資材名","税抜組合員単価","税込組合員単価","企画年月回","配達年月回","企画単協 エフ","企画単協 さが","企画単協 ララ","企画単協 おおいた","企画単協 水光社","企画単協 みやざき","企画単協 かごしま","企画単協 おきなわ","税コード","消費税率","単価計算区分","税抜計算区分","税込計算区分","禁則チェック区分","エラーコード","削除フラグ","作成日","作成時刻","更新日","更新時刻"
"301","1005405","100317290","2000000432908","1","02","01","02","09","02","フレンドリーバナナ(フィリピン産)","750g",1,"2","0",,288,311,"2023041"," ","0","0","0","0","0","0","0","1","080",0.08,"2","2","1","1"," ","0","20230117","100742","20230309","102624"
"301","1039130","100499798","2000000648941","1","02","01","02","09","02","フレンドリーバナナ(フィリピン産)","500g(3?5本)",1,"2","0",,278,300,"2023041"," ","0","0","0","0","0","0","0","1","080",0.08,"2","2","1","1"," ","0","20230117","100742","20230309","102624"
"301","1355180","800022912","2008000229122","1","01","01","01","02","02","貝割大根(大分県産)","1パック",20,"2","0",,36,38,"2023041"," ","0","0","0","1","0","0","0","0","080",0.08,"2","2","1","1"," ","0","20230117","100742","20230309","102624"
"301","1395165","800055699","2000000678788","1","01","01","00","05","02","たまねぎ(北海道産)","300g(大小混)",1,,"0",,138,149,"2023041"," ","0","0","0","0","0","0","0","1","080",0.08,"2","2","1","1"," ","0","20230117","100742","20230309","102624"
"301","1409182","100433518","2000000601557","2","01","01","00","05","00","よくねたいも〈北あかり〉(北海道産)","500g(大小混)",1,"1","0",,258,278,"2023041"," ","0","0","0","0","0","0","0","1","080",0.08,"2","2","1","1"," ","0","20230302","130441","20230309","102458"
"301","1417550","800072337","2008000723378","1","01","01","00","01","05","ほうれん草(長崎県産)","150g",1,"2","0",,138,149,"2023041"," ","0","0","1","0","0","0","0","0","080",0.08,"2","2","1","1"," ","0","20230117","100742","20230309","102624"
四. 转换工具
- 复制代码创建html文件,拿来就用。😋不用配置环境
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>CSV转换SQL工具</title>
</head>
<body><div style="display: flex;"><label for="schema">指定schema</label><select id="schema"><option value=""></option><option value="nosan">nosan 农业</option><option value="sozai">sozai 蔬菜</option><option value="shokan">shokan 商管</option></select></div><hr><label for="removeBlank">是否去除字符串中的空白</label><input id="removeBlank" type="checkbox" /><hr><label for="csv">请上传csv文件</label><input id="csv" type="file" accept=".csv" multiple /><hr><button id="transform">csv转sql开始</button><hr><!-- 显示错误消息的区域 --><div id="error" style="color: red;"></div>
</body>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script>// 是否去除字符串空白flaglet removeBlank = false;$(function() {transformEvent();});function transformEvent() {$("#transform").click(function() {removeBlank = $("#removeBlank").prop("checked");const schema = $("#schema").val();if(!schema) {alert("请指定schema!");return;}// 清空所有的内容$("#error").empty();// 获取上传的所有csv文件const csvFileList = $("#csv").get(0).files;for(csvfile of csvFileList) {const sqlList = [];/*将上传的文件名称当做表名注意:下面这行代码不能放到reader的load回调里面,否则生成的csv的文件名会相同*/const tableName = csvfile.name.split(".")[0];// 创建csv文件读取对象const reader = new FileReader();// 指定要读取的CSV的编码reader.readAsText(csvfile, "Shift_JIS");reader.addEventListener('load', ({target: {result: csvText}}) => {// 获取出标题栏之外的csv内容数据const hanleCSVData = csvText.slice(csvText.indexOf('\n') + 1).split('\n');// 如果上传的csv文件只有标题没有数据if(!hanleCSVData || hanleCSVData.length === 1 && !hanleCSVData[0].trim()) {const newHtml = $("#error").html() + "<br />" + tableName + ".csv文件没有内容!";$("#error").html(newHtml);return;}for (const csvRowData of hanleCSVData) {if (!csvRowData) {continue;}// 构造插入SQLlet insertSql = SqlUtils.createBaseSql(schema, tableName);// 创建要插入sql的值const sqlValueStr = SqlUtils.addCsvRowToSqlItem(csvRowData);// 补足自定义项目后,构建const insertSqlStr = SqlUtils.customeSqlHandle(insertSql + sqlValueStr);sqlList.push(insertSqlStr);}// 下载转换之后的sql文件fileDownload(sqlList, `${tableName}.sql`);});}});};class SqlUtils {// 获取当前时间 yyyy/MM/dd HH:mm:ssstatic nowDate = new Date().toLocaleString();// 是否去除字符串中的空白static trimStrFlg = true;// 创建基本的插入SQL语句static createBaseSql(schema, tableName) {// 因为csv项目和表项目完全一致,因此可以省略项目return `INSERT INTO [${schema}].${tableName} VALUES (`;}static addCsvRowToSqlItem(csvRowData) {let sqlItem = "";// 获取CSV所有的列const csvItemList = csvRowData.split(",");const newcsvItemList = [];// 对csv项目进行特殊处理for (const csvItem of csvItemList) {if(!!csvItem) {/*如果需要去除空格,并且是csv中的字符串项目的话*/if (removeBlank && csvItem.includes("\"")) {// "测试 " ===> '测试'newcsvItemList.push("\'" + csvItem.replaceAll('"', '').trim() + "\'");} else {// 插入的是数字newcsvItemList.push(csvItem);}} else {// 如果csv项目为空项目则插入NULLnewcsvItemList.push('NULL');}}// 将list转换为以 , 分隔的字符串,并把 " 替换为 ' ,因为sql插入时只能用 'return newcsvItemList.join(", ").replaceAll('\"', '\'') + ", ";}static customeSqlHandle(insertSql) {// 插入时间const SKSI_NCHJ = `'${SqlUtils.nowDate}'`;// 更新时间const KUSHN_NCHJ = `'${SqlUtils.nowDate}'`;// 添加换行符return `${insertSql}${SKSI_NCHJ}, ${KUSHN_NCHJ});\r\n`;}}function fileDownload(data, fileName) {// 创建文件下载的urlconst insertSqlStrBlob = new Blob(data, {type: "application/sql"});const src = URL.createObjectURL(insertSqlStrBlob);// 创建a标签const aElement = document.createElement('a');aElement.download = fileName;aElement.style.display = 'none';aElement.href = src;// 将a标签添加到页面上手动触发点击事件,从而触发文件下载document.body.appendChild(aElement);aElement.click();document.body.removeChild(aElement);URL.revokeObjectURL(src);}
</script>
</html>