项目需求
刚接手了一个需求,今天终于做完了。其中的一个小需求折腾了我半天,最终采用了第二套方案给解决了,真是一波三折啊。这个需求的背景是这样的:页面上,angularjs的ng-repeat标签展示的一个list。list的每个item前面有一个多选框,如果选中的话,我们将这些被选中的item放在csv中进行展示。
需求分析
方案一:
最初,我脑海中的闪现的方案是这样的:将这些item取出来,最终变为json格式形式传递给后台(此处用的是ajax异步发送)。然后将这些信息存储到服务器的一个临时文件中。然后在ajax的回调函数中,利用form表单重新发送一个请求,这个请求去下载那个临时文件。最终文件通过流的方式输送给浏览器,浏览器就会以文件下载的形式展示出来。
其实,在确定方案一的时候还是出现了一些小插曲的:最开始,我并没有用到回调函数,而是直接在一个方法中先将item放到文件中,在读文件以文件流的方式,然后天真地以为浏览器就可以以文件下载的方式弹出一个框让你来下载csv文件了。可是,有经验的小伙伴你们应该想到了,这样是行不通的,ajax最终的返回形式是string或者xml。是不可以放回流数据的。因此,才有了后来在ajax的回调函数中动态地构建一个form来提交一个请求。
万万没想到,最终方案一还是失败了。本以为到了上一步,已经大功告成了。可是,在这里还是发生了一个致命的bug。当点击下载按钮后,突然跳到了登录页面,我心里一惊,怎么会这样,于是我打开调试窗口一看,Whoops! Lost connection to http://localhost:8080/app;和本地服务器失去了连接,我想可能是回调函数中form的提交造成了。又换了构造iframe来尝试同样的错误,在网上资料多是说缺少jackson的jar包,可以我打开项目一看jar包是全的。这个bug对我来说太棘手了,因此我打算放弃这个方案。我又开始想有没有其他的方法呢。于是有了方案二的诞生。
方案二
我首先想到,其实这个东西就是页面的信息,干嘛非要经过服务器,多此一举呢,要是前端能够直接生成cvs文件多好呢。没想到啊,船到桥头自然直,真的被我找到了。利用a标签可以直接生成下载的对话框让你来下载cvs文件。
demo.html:
<!DOCTYPE html>
<html>
<head>
<title>js下载csv文件</title>
<meta charset="utf-8">
<script>
function clickDownload(aLink){
var str = "col1,列2,col3\nvalue1,value2,value3";
str = encodeURIComponent(str);
aLink.href = "data:text/csv;charset=utf-8,\ufeff"+str;
<!--aLink.click();-->
}
</script>
</head>
<body>
<a id="test" onclick="clickDownload(this)" download="download.csv" href="#">download</a>
</body>
</html>
妈呀,真的可以啊。这下不经过服务器,效率肯定也会提高很多。只要结合我的内容,把要输出的str拼接出来就好了。真的是太棒了。
万万没想到有发生了一点小插曲:
- 首先,angularjs中的this和我们js中的this的概念是不同的,在angularjs中this标签指的是当前的
scope对象。而在js中,this就是指当前对象。因此,想获得angularjs中的当前对象,就要往函数中传
event对象。然后在方法体中
event.target就表示当前点击事件的对象。−以为做到上不就搞定了吗,一般情况下的确是ok了。但是,万万没想到啊,我们得a标签里面还需要嵌套一个button标签,为的是保证页面的样式。当我,点击触发了事件,浏览器却没有下载动作的发生。于是,我有打开调试器发现,当前对象的target已经变味了button。target里面的parentNode和parentElement才是a标签,于是把方法体中的
event.target变为$event.target.parentNode。哈哈哈,功夫不负有心人,大功告成!
最后,本文中的相关知识点我也会整理出来发在我的博客中,欢迎关注!