前端导出文件大部分还是通过服务器端的方式生成文件,然后传递到客户端。但很多情况下当我们导出CSV时并不需要后端参与,甚至没有后端。
做过WebGIS的同学经常会碰到这种场景,用户的兴趣点数据以csv文件形式上传到web应用中以表格形式展示,并可以编辑属性信息,编辑完成后需要将数据下载到本地。特别是对一些敏感数据,用户不希望传递到应用服务器端,整个过程完全在客户端进行。
上传过程我们暂且不讨论,只讨论生成CSV以及下载过程。
CSV的生成
问题一:如何分行分列?
思路:分行使用“\n”,分列使用","
实际应用中发现导出的csv用excel打开后,列可以分开但行无法分开。
解决方法是,将生成的csv字符串使用encodeURIComponent编码
问题二:字段值中含有特殊符号影响csv文件的正确解读,如:“,”,"\n"
思路:将含有特殊符号的字段用双引号包装起来,如:a,b => "a,b"
if (value && /[,\r\n]/g.test(value)) {
value = textField + value + textField;
}
实际应用发现少考虑了一种情况,如果字段值中含有‘ " ’这个符号,经过上方代码处理反而会出现问题:a"b => "a"b"。显然是语法错误。
解决方法是将"换成"",a"b => "a""b"
if (value && /[",\r\n]/g.test(value)) {
value = textField + value.replace(/(")/g, '""') + textField;
}
在解决以上问题后生成CSV字符串代码如下
//_outFields: 字段名称数组
exports.createCSVStr = function(data, _outFields) {
var textField = '"';
var content = "";
var len = 0,
n = 0,
comma = "",
value = "";
try {
array.forEach(_outFields, function(_field) {
content = content + comma + _field;
comma = ",";
});
content = content + "\r\n";
len = data.length;
n = _outFields.length;
for (var i = 0; i < len; i++) {
comma = "";
for (var m = 0; m < n; m++) {
var _field = _outFields[m];
value = data[_field];
if (!value && typeof value !== "number") {
value = "";
}
if (value && /[",\r\n]/g.test(value)) {
value = textField + value.replace(/(")/g, '""') + textField;
}
content = content + comma + value;
comma = ",";
}
content = content + "\r\n";
}
} catch (err) {
console.error(err);
content = "";
}
return content;
};
问题三:如果字段中含有希伯来文、法语、德语等文字('éà; ça; 12\nà@€; çï; 13'
),导出的csv文件在Excel中打开后,这些文字呈现出乱码
解决方法:严格来说这并不是csv文件的问题,而是Excel处理文件编码方式问题,Excel默认并不是以UTF-8来打开文件,所以在csv开头加入BOM,告诉Excel文件使用utf-8的编码方式。
var csvStr = BOM + csvStr;
实际应用中发现,这种处理方式在windows中的Excel中打开后可以正常显示,但在mac上的Excel无法正确显示。目前没有完全的解决方案,但mac中可以使用自带的Numbers软件打开,不会出现乱码问题。
CSV的下载方式
问题一:如何在解决不同浏览器中的下载问题?
思路:
- IE10以下,利用execCommand方法来保存csv文件var oWin = window.top.open("about:blank", "_blank");
oWin.document.write(encodeURIComponent(text));
oWin.document.close();
oWin.document.execCommand('SaveAs', true, filename);
oWin.close();在实际应用中浏览器会打开一个新窗口,并弹出保存文件对话框,而对话框中保存类型时,只有html和text两项可选,此时需要在文件名中手动加上“.csv”后缀
- IE10以及Edge浏览器使用navigator.msSaveBlob(blob);虽然这些浏览器也支持上面的方法,但可以避免上面遇到的问题。var BOM = "\uFEFF";
var csvData = new Blob(, { type: 'text/csv' });
navigator.msSaveBlob(csvData, filename);msSaveBlob是IE的私有方法,只有IE10及以上和Edge浏览器支持。
- Firefox、Chrome、Safari浏览器中使用a标签,利用html5中增加的download属性来下载csvvar link = html.create("a", {
href: 'data:attachment/csv;charset=utf-8,' + BOM + encodeURIComponent(text),
target: '_blank',
download: filename
}, this.domNode);
if (has('safari')) {
// # First create an event
var click_ev = document.createEvent("MouseEvents");
// # initialize the event
click_ev.initEvent("click", true /* bubble */ , true /* cancelable */ );
// # trigger the evevnt/
link.dispatchEvent(click_ev);
} elseDOM特性节点ATTRIBUTE - 小火柴的蓝色理想 阅读原文»定义
每个元素都有一个或多个特性,这些特性的用途是给出相应元素或内容的附加信息。实质上,特性节点就是存在于元素的attributes属性中的节点。
特征
nodeType:2
nodeName:特性的名称
nodeValue:特性的值
parentNode:null
childNode:chrome、firefox下为undefined,safari下为Text,IE9+下为子元素的特性名,IE8-下报错
[注意]尽管Attribute也是节点,但却不被认为是DOM文档树的一部分,开发人员常用getAttribute()、setAttribute()、removeAttribute(),很少直接引用特性节点<div id="box"></div>
<script>
var oBox = document.getElementById('box');
var oAttr = oBox.attributes;
//(chrome\safari\IE9+\firefox) 2 id box null
//(IE7-) 2 onmsanimationiteration null null
console.log(oAttr[0].nodeType,oAttr[0].nodeName,oAttr[0].value,oAttr[0].parentNode)
//(chrome\firefox) undefined
//(safari) Text
//(IE9+) box
//(IE8-) 报错
console.log(oAttr[0].childNodes[0])
</script>特性节点属性
Attr对象有3个属性:name、value和specified
【1】name是特性名称(与nodeName的值相同)
【2】value是特性的值(与nodeValue的值相同)
【3】specified是一个布尔值,用以区别特性是在代码中指定的,还是默认的。这个属性的值如果为true,则意味着要么是在HTML中指定了相应特性,要么是通过setAttribute()方法设置了该属性。在IE中,所有未设置过的特性的该属性值都为false,而在其他浏览器中根本不会为这类特性生成对应的特性节点<div class="box" id="box"></div>
<script>
var oBox = document.getElementById('box');
var oAttr = oBox.attributes;
//(chrome\safari\IE8+)class class true
//(firefox)id id true
//(IE7-)onmsanimationiteration onmsanimationiteration true
console.log(oAttr[0].name,oAttr[0].nodeName,oAttr[0].name == oAttr[0].nodeName)
//IE7- "null" null false
//其他浏览器 box box true
console.log(oAttr[0].value,oAttr[0].nodeValue,oAttr[0].value == oAttr[0].nodeValue)
//IE7- false
//其他浏览器 true
console.log(oAttr[0].specified)//true
</script><div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.attributes.id.specified)//true
console.log(oBox.attributes.onclick.specified)//在IE7-浏览器下会返回false,在其他浏览器下会报错
</script>特性属性attributes
Element类型是使用attributes属性的唯一一个DOM节点类型。attributes属性中包含一个NamedNodeMap,与NodeList类似,也是一个动态的集合。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。
【attributes属性的四个方法】
[a]getNamedItem(name):返回nodeName属性等于name的节点
removeNamedItem(name):从列表中移除nodeName属性等于name的节点
[c]setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引
[d]item(pos):返回位于数字pos位置处的节点,也可以用方括号法[]简写<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.attributes);//NamedNodeMap {0: class, 1: id, 2: name, 3: index, 4: title}
var getTest = oBox.attributes.getNamedItem("index");
console.log(getTest);//index = "123"
var removeTest = oBox.attributes.removeNamedItem("class");
console.log(removeTest);//class = "box"
console.log(oBox.attributes.getNamedItem("class"));//null
console.log(oBox.attributes.setNamedItem(removeTest));//null
console.log(oBox.attributes.setNamedItem(getTest));//index = "123"
console.log(oBox.attributes.item(0));//id="box"(每个浏览器获取的不一样)
</script>attributes属性中包含一系列节点,每个节点的nodeName就是特性的名称,节点的nodeValue就是特性的值
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
var oBox = document.getElementById('box');
console.log(oBox.attributes);//NamedNodeMap {0: class, 1: id, 2: name, 3: index, 4: title}
console.log(oBox.attributes.id.nodeName);//"id"
console.log(oBox.attributes.id.nodeValue);//"box"
</script>【特性遍历】
attributes属性主要用于特性遍历。在需要将DOM结构序列化为XML或HTML字符串时,多数都会涉及遍历元素特性
function outputAttributes(element){
var pairs = new Array(),attrName,attrValue,i,len;
for(i = 0,len=element.attributes.length;i<len;i++){
attrName = element.attributes.nodeName;
attrValue = element.attributes.nodeValue;
pairs.push(attrName +"=\"" + attrValue + "\"");
}
return pairs.join(" ");
}[注意1]针对attributes对象中的特性,不同浏览器返回的顺序不同
<div class="box" id="box" name="abc" index="123" title="test"></div>
<script>
function outputAttributes(element){
var pairs = new Array(),attrName,attrValue,i,len;
for(i = 0,len=element.attributes.length;i<len;i++){
attrName = element.attributes.nodeName;
attrValue = element.attributes.nodeValue;
pairs.push(attrName +"=\"" + attrValue + "\"");
}
return pairs.join(" ");
}
//(chrome\safari)class="box" id="box" name="abc" index="123" title="test"
//(firefox)title="test" index="123" name="abc" id="box" class="box"
//(IE8+)title="test" class="box" id="box" index="123" name="abc"阅读更多内容
没有评论:
发表评论