依赖模块
xlwt下载:pip install xlwt
后台模块
view.py
# 导出Excel文件 def export_excel(request): city = request.POST.get('city') print(city) list_obj=place.objects.filter(city=city) # 设置HTTPResponse的类型 response = HttpResponse(content_type='application/vnd.ms-excel') response['Content-Disposition'] = 'attachment;filename='+city+'.xls' """导出excel表""" if list_obj: # 创建工作簿 ws = xlwt.Workbook(encoding='utf-8') # 添加第一页数据表 w = ws.add_sheet('sheet1') # 新建sheet(sheet的名称为"sheet1") # 写入表头 w.write(0, 0, u'地名') w.write(0, 1, u'次数') w.write(0, 2, u'经度') w.write(0, 3, u'纬度') # 写入数据 excel_row = 1 for obj in list_obj: name = obj.place sum = obj.sum lng = obj.lng lat = obj.lat # 写入每一行对应的数据 w.write(excel_row, 0, name) w.write(excel_row, 1, sum) w.write(excel_row, 2, lng) w.write(excel_row, 3, lat) excel_row += 1 # 写出到IO output = BytesIO() ws.save(output) # 重新定位到开始 output.seek(0) response.write(output.getvalue()) return response
前端模块
<button id="export_excel" type="button" class="btn btn-primary col-sm-5" style="margin-left: 10px" >导出excel</button>
$("#export_excel").click(function () { var csrf=$('input[name="csrfmiddlewaretoken"]').val(); const req = new XMLHttpRequest(); req.open('POST', '/export_excel/', true); req.responseType = 'blob'; req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //设置请求头 req.send('city='+$('#city').val()+"&&csrfmiddlewaretoken="+csrf); //输入参数 req.onload = function() { const data = req.response; const a = document.createElement('a'); const blob = new Blob([data]); const blobUrl = window.URL.createObjectURL(blob); download(blobUrl) ; }; });
function download(blobUrl) { var city = $("input[name='city']").val(); const a = document.createElement('a'); a.style.display = 'none'; a.download = '<文件命名>'; a.href = blobUrl; a.click(); document.body.removeChild(a); }
补充知识:Python Django实现MySQL百万、千万级的数据量下载:解决memoryerror、nginx time out
前文
在用Django写项目的时候时常需要提供文件下载的功能,而Django也是贴心提供了几种方法:FileResponse、StreamingHttpResponse、HttpResponse,其中FileResponse和StreamingHttpResponse都是使用迭代器迭代生成数据的方法,所以适合传输文件比较大的情况;而HttpResponse则是直接取得数据返回给用户,所以容易造成memoryerror和nginx time out(一次性取得数据和返回的数据过多,导致nginx超时或者内存不足),关于这三者,DJango的官网也是写的非常清楚,连接如下:https://docs.djangoproject.com/en/1.11/ref/request-response/
那正常我们使用的是FileResponse和StreamingHttpResponse,因为它们流式传输(迭代器)的特点,可以使得数据一条条的返回给客户端,文件随时中断和复传,并且保持文件的一致性。
FileResponse和StreamingHttpResponse
FileResponse顾名思义,就是打开文件然后进行传输,并且可以指定一次能够传输的数据chunk。所以适用场景:从服务端返回大文件。缺点是无法实时获取数据库的内容并传输给客户端。举例如下:
def download(request): file=open('path/demo.py','rb') response =FileResponse(file) response['Content-Type']='application/octet-stream' response['Content-Disposition']='attachment;filename="demo.py"' return response
从上可以发现,文件打开后作为参数传入FileResponse,随后指定传输头即可,但是很明显用这个来传输数据库就不太方便了,所以这边推介用StreamingHttpResponse的方式来传输。
这里就用PyMysql来取得数据,然后指定为csv的格式返回,具体代码如下:
# 通过pymysql取得数据 import pymysql field_types = { 1: 'tinyint', 2: 'smallint', 3: 'int'} #用于后面的字段名匹配,这里省略了大多数 conn = pymysql.connect(host='127.0.0.1',port=3306,database='demo',user='root',password='root') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute(sql) #获取所有数据 data = cursor.fetchall() cols = {} #获取所有字段 for i,row in enumerate(self.cursor.description): if row[0] in cols: cols[str(i)+row[0]] = field_types.get(row[1], str(row[1])) #这里的field_type是类型和数字的匹配 cols[row[0]] = field_types.get(row[1], str(row[1])) cursor.close() conn.close() #通过StreamingHttpResponse指定返回格式为csv response = StreamingHttpResponse(get_result_fromat(data, cols)) response['Content-Type'] = 'application/octet-stream' response['Content-Disposition'] = 'attachment;filename="{0}"'.format(out_file_name) return response #循环所有数据,然后加到字段上返回,注意的是要用迭代器来控制 def get_result_fromat(data, cols): tmp_str = "" # 返回文件的每一列列名 for col in cols: tmp_str += '"%s",' % (col) yield tmp_str.strip(",") + "\n" for row in data: tmp_str = "" for col in cols: tmp_str += '"%s",' % (str(row[col])) yield tmp_str.strip(',') + "\n"
整个代码如上,大致分为三部分:从mysql取数据,格式化成我们想要的格式:excel、csv、txt等等,这边指定的是csv,如果对其他格式也有兴趣的可以留言,最后就是用StreamingHttpResponse指定返回的格式返回。
实现百万级数据量下载
上面的代码下载可以支持几万行甚至十几万行的数据,但是如果超过20万行以上的数据,那就比较困难了,我这边的剩余内存大概是1G的样子,当超过15万行数据(大概)的时候,就报memoryerror了,问题就是因为fetchall,虽然我们StreamingHttpResponse是一条条的返回,但是我们的数据时一次性批量的取得!
如何解决?以下是我的解决方法和思路:
用fetchone来代替fetchall,迭代生成fetchone
发现还是memoryerror,因为execute是一次性执行,后来发现可以用流式游标来代替原来的普通游标,即SSDictCursor代替DictCursor
于是整个代码需要修改的地方如下:
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) ===>
cursor = conn.cursor(cursor=pymysql.cursors.SSDictCursor)
data = cursor.fetchall() ===>
row = cursor.fetchone()
def get_result_fromat(data, cols): tmp_str = "" # 返回文件的每一列列名 for col in cols: tmp_str += '"%s",' % (col) yield tmp_str.strip(",") + "\n" for row in data: tmp_str = "" for col in cols: tmp_str += '"%s",' % (str(row[col])) yield tmp_str.strip(',') + "\n" =====> def get_result_fromat(data, cols): tmp_str = "" for col in cols: tmp_str += '"%s",' % (col) yield tmp_str.strip(",") + "\n" while True: tmp_str = "" for col in cols: tmp_str += '"%s",' % (str(row[col])) yield tmp_str.strip(',') + "\n" row = db.cursor.fetchone() if row is None: break
可以看到就是通过while True来实现不断地取数据下载,有效避免一次性从MySQL取出内存不足报错,又或者取得过久导致nginx超时!
总结
关于下载就分享到这了,还是比较简单的,谢谢观看~希望能给大家一个参考,也希望大家多多支持。
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。