采用文件流方式导入导出文件,不会在服务端产生file
,节约服务端存储空间。
excel
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
# WorkShopApi/urls.py 主路由
from django.conf import settings
from django.views.static import serve
from django.urls import path, include, re_path
urlpatterns = [
path('api/backend/', include('apps.backend.urls')),
]
# apps/backend/urls.py 分路由
from apps.backend import views
from django.urls import path, include
from rest_framework import routers
router = routers.SimpleRouter()
router.register('user_auth', views.UserAuthView, basename='user_auth')
urlpatterns = [
path('', include(router.urls)),
path('regulations/download/', views.regulation_download, name='regulation_download'),
]
# apps/backend/views.py
import io
import xlwt
from django.http import HttpResponse
from django.utils.encoding import escape_uri_path
def regulation_download(request, *args, **kwargs):
# 1.获取从前端传输过来的数据,并通过json序列化转成list格式
excel_data_list = json.loads(request.body)
# 2.创建一个工作簿(workbook),并设置编码
workbook = xlwt.Workbook(encoding='utf-8')
# 3.创建一个工作表(worksheet)
worksheet = workbook.add_sheet("My Worksheet")
# 3.1 设置列宽
worksheet.col(0).width = 256 * 8 # 8个字符0的宽度
worksheet.col(1).width = 256 * 12
worksheet.col(2).width = 256 * 30
worksheet.col(3).width = 256 * 30
worksheet.col(4).width = 256 * 30
worksheet.col(5).width = 256 * 20
worksheet.col(6).width = 256 * 16
# 3.2 设置表头行高
style = xlwt.easyxf('font:height 360;') # 18pt,类型小初的字号
row = worksheet.row(0)
row.set_style(style)
# 4.写表头部分
# 4.1 表头数据
header_list = ['id', '法规类型', '法规名称', '法规网站地址', '主要内容', '实施日期', '备注']
# 4.2 表头样式设置(可选)
style = xlwt.XFStyle() # 初始化样式
font = xlwt.Font() # 为样式创建字体
font.name = "Times New Roman"
font.bold = True # 加粗
font.underline = False # 下划线
font.italic = False # 斜体字
style.font = font # 设定样式
# 4.3 写表头
for header_data in enumerate(header_list):
worksheet.write(0, header_data[0], header_data[1], style)
# 4.4 写表格内容数据
for index, row_data in enumerate(excel_data_list):
worksheet.write(index + 1, 0, row_data.get('id')) # 第一行为表头
worksheet.write(index + 1, 1, row_data.get('regulation_type_text'))
worksheet.write(index + 1, 2, row_data.get('title'))
worksheet.write(index + 1, 3, row_data.get('url'))
worksheet.write(index + 1, 4, row_data.get('content_main'))
worksheet.write(index + 1, 5, row_data.get('effective_date'))
worksheet.write(index + 1, 6, row_data.get('meno'))
# 表头行高
row = worksheet.row(index + 1)
row.set_style(style)
# 5.保存文件
# 5.1 保存到本地文件:保存文件目录级别在项目目录下,与apps同级
# workbook.save("test.xls")
# 5.2 保存文件流到内存
sio = io.BytesIO()
workbook.save(sio)
sio.seek(0)
# 5.3 返回数据给前端
# 5.3.1 获取文件流,设置文件类型
response = HttpResponse(sio.getvalue(), content_type='application/vnd.ms-excel')
# 5.3.2 生成文件名称,名称包含法规导出+年月日时分秒数字的组合,避免重复
file_name = '法规导出{}'.format(datetime.now().strftime(format('%Y%m%d%H%M%S')))
# 5.3.3 配置Response Headers中`Content-Disposition`信息,支持中文
response['Content-Disposition'] = "attachment;filename={}.xls".format(escape_uri_path(file_name))
# 5.3.4 必须将`Content-Disposition`暴露给前端,否则前端获取不到信息
response['Access-Control-Expose-Headers'] = "Content-Disposition"
# 5.3.5 返回数据给前端
response.write(sio.getvalue())
return response
Content-Disposition
中文件乱码不显示问题:https://www.cnblogs.com/hanfe1/p/12161231.htmlContent-Disposition
在浏览器中能看到但在axios
的返回值response中没有显示且只有一个content-type: “application/octet-stream”问题:https://blog.csdn.net/qq_43617906/article/details/129369193django
导入导出excel实践:https://www.cnblogs.com/xiugeng/p/10912417.htmlvue
文件代码<template>
<el-row>
<el-table :data="state.regulationList" size="default" border style="width: fit-content" table-layout="auto"
show-overflow-tooltip @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"/>
……
</el-table>
</el-row>
<el-row :justify="'end'" class="menus">
<el-dropdown class="menu" trigger="click" @command="handleCommand">
<el-button>更多操作
<el-icon class="el-icon--right">
<ArrowDown/>
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="doBatchImport">
<el-upload
class="upload-demo"
:action="excelUploadUrl"
multiple
:limit="3"
accept=".xls"
:on-success="upLoadSuccess"
>
<span>批量导入</span>
</el-upload>
</el-dropdown-item>
<!--核心代码-->
<el-dropdown-item command="doExportExcel">
导出EXCEL
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button class="menu" @click="doBatchDelete" command="delete">批量删除</el-button>
</el-row>
</template>
<script setup>
import {reactive, getCurrentInstance, onMounted, onBeforeUnmount} from "vue";
import {ElMessage} from "element-plus";
import {useStore} from "vuex";
import {saveAs} from 'file-saver'
const {proxy} = getCurrentInstance();
function handleSelectionChange(valueList) {
state.selectList = valueList;
// console.log(valueList)
}
function handleCommand(command) {
// 1.将数据传到后台,生成文件流,return response
if (command === 'doExportExcel') {
// 1.发送post请求,携带数据到后端
proxy.$axios.post(
`/api/backend/regulations/download/`,
state.selectList, // 多选的数据列表
{responseType: 'arraybuffer'} // 必须添加此选项,将responseType设为arraybuffer
).then((res) => {
// 2.获取文件名称
const contentDisposition = res.headers.get('content-disposition')
const fileNameRegexp = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = fileNameRegexp.exec(contentDisposition);
const filename = (matches != null && matches[1] ? decodeURIComponent(matches[1].replace(/['"]/g, '')) : 'unknown');
// 2.导出到本地保存
const blob = new Blob([res.data], {type: 'application/vnd.ms-excel'});
saveAs(blob, filename);
})
}
}
</script>
content-disposition
中的文件名称:https://www.5axxw.com/questions/simple/bapmy1vue
根据后端获取的文件流生成文件 并下载:https://www.5axxw.com/questions/simple/n9he0jexcel
# apps/backend/views.py
class RegulationView(ModelViewSet):
queryset = models.Regulations.objects.all()
serializer_class = RegulationModelSerializer
pagination_class = MyPage
@action(detail=False, methods=['post'], url_path="upload")
def upload(self, request):
""" 上传excel文件并将数据写入到数据库中 """
# 1.读取上传的文件
# request.POST,request.GET,request.FILES
upload_object = request.FILES.get('file')
# 2.判断文件大小
if upload_object.size > 2 * 1024 * 1024: # 如果文件超过2M,提示:上传文件超过2M,太大了
return Response({
'code': return_code.ERROR,
'msg': '上传文件超过2M,太大了'
})
# 3.获取文件类型的后缀名,如果不是xls,则提示:上传文件不是excel标准格式【.xls】文件,请重新上传
file_type_name = upload_object.name.split('.')[1]
if file_type_name != 'xls':
return Response({
'code': return_code.ERROR,
'msg': '上传文件不是excel标准格式【.xls】文件,请重新上传'
})
# 4.获取文件数据,并存入数据库
# 4.1 获取Excel文件读取器,加上utf-8编码可防止遇到中文字符乱码
data = xlrd.open_workbook(file_contents=upload_object.read(), encoding_override='utf-8')
# 4.2 通过索引获取第一个sheet工作簿
table = data.sheet_by_index(0)
nrows = table.nrows # 总行数
# 4.3 获取table数据列表,item为每行数据
data_list = [table.row_values(i) for i in range(nrows)]
# 4.4 返回前端的数据,即将要在前端state.dataList中添加的数据,在前端进行展示
res_data_list = []
regulation_type_class_map = {1: 'success', 2: '', 3: 'danger'}
regulation_type_text_map = {1: '政府采购', 2: '招标投标', 3: '其他'}
for index, item in enumerate(data_list):
if table.cell(index, 5).ctype == 3:
# 4.5 如果数据为日期格式,将数据格式转化为:2023年8月18日
date_value = xlrd.xldate_as_tuple(table.cell(index, 5).value, data.datemode)
effective_date = date(*date_value[:3]).strftime('%Y年%m月%d日')
# 4.6 根据数据库对应字段获取每行数据,写入数据库中
row_data_dict = {
'regulation_type': item[0],
'title': item[1],
'url': item[2],
'content_main': item[3],
'meno': item[4],
'effective_date': effective_date,
}
models.Regulations.objects.create(**row_data_dict)
# 4.7 返回前端的数据字段增加regulation_type_class、regulation_type_text,用于前端渲染
row_data_dict['regulation_type_class'] = regulation_type_class_map[item[0]]
row_data_dict['regulation_type_text'] = regulation_type_text_map[item[0]]
res_data_list.append(row_data_dict)
return Response({
'code': return_code.SUCCESS,
'data': res_data_list,
'msg': '批量导入数据成功'
})
vue
文件代码<template>
<el-row>
<el-table :data="state.regulationList" size="default" border style="width: fit-content" table-layout="auto"
show-overflow-tooltip @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"/>
……
</el-table>
</el-row>
<el-row :justify="'end'" class="menus">
<el-dropdown class="menu" trigger="click" @command="handleCommand">
<el-button>更多操作
<el-icon class="el-icon--right">
<ArrowDown/>
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="doBatchImport">
<!--核心代码 accept:文件格式,action:上传路由-->
<el-upload
class="upload-demo"
:action="excelUploadUrl"
multiple
:limit="3"
accept=".xls"
:on-success="upLoadSuccess"
>
<span>批量导入</span>
</el-upload>
</el-dropdown-item>
<el-dropdown-item command="doExportExcel">
导出EXCEL
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button class="menu" @click="doBatchDelete" command="delete">批量删除</el-button>
</el-row>
</template>
<script setup>
function upLoadSuccess(res) {
// console.log(res)
if (res.code === 0) {
// 循环后端返回数据,逐条赋值到state.regulationList
res.data.data.forEach(function (value, index) {
state.regulationList.push(value)
})
ElMessage.success('批量导入成功')
} else {
ElMessage.error('批量导入失败')
}
}
</script>
本文简要介绍了django restframework中文件流方式导入导出excel文件的方法。