0
点赞
收藏
分享

微信扫一扫

【fastapi】以流模式生成并返回电子表格(不占用存储)


最近一直在用FastApi开发Web系统,经常需要遇到数据导出的需求。
分享一个我自己使用的导出函数。
优点
1.以数据流生成并返回给前端下载,不占用服务器存储。
2.可以自定义表头和数据样式
代码里的注释都标记完整了,可以直接使用。

import xlwt
from io import BytesIO
from urllib.parse import quote
from fastapi.responses import StreamingResponse


def export_exl(header, data=None, data_col=None, file_name='download', need_order=False):
    """
    以流的形式导出到excel
    :param header: ['列名1', '列名2']
    :param data: [{'a': 1, 'b': 2}]			数据集,与header、data_col对应
    :param data_col: ['与列名对应的字段1', '与列名对应的字段2']
    :param file_name: 文件名称
    :param need_order: 是否插入序号
    :return:
    """
    def set_style():
        """
        设置样式
        :return:
        """
        # 居中设置
        alignment = xlwt.Alignment()
        alignment.horz = xlwt.Alignment.HORZ_CENTER
        alignment.vert = xlwt.Alignment.VERT_CENTER
        alignment.wrap=1 #设置自动换行

        # 设置表头字体样式
        head_style = xlwt.XFStyle()
        font = xlwt.Font()
        font.name = 'Times New Roman'   # 字体
        font.height = 20 * 16           #字体大小
        font.bold = True                # 字体加粗
        font.colour_index=9             #字体颜色
        # 设置背景颜色
        pattern = xlwt.Pattern()
        # 设置背景颜色的模式
        pattern.pattern = xlwt.Pattern.SOLID_PATTERN
        # 背景颜色
        pattern.pattern_fore_colour = 40
        head_style.pattern=pattern
        head_style.font = font              # 设置字体
        head_style.alignment = alignment    # Add Alignment to Style

        # 设置表中内容样式
        cont_style = xlwt.XFStyle()
        font = xlwt.Font()
        font.name = 'Times New Roman'   # 字体
        font.bold = False               # 字体加粗
        cont_style.font = font              # 设置字体
        cont_style.alignment = alignment    # Add Alignment to Style

        # 设置单元格边界
        borders = xlwt.Borders()
        borders.left = xlwt.Borders.THIN
        borders.right = xlwt.Borders.THIN
        borders.top = xlwt.Borders.THIN
        borders.bottom = xlwt.Borders.THIN
        head_style.borders = borders
        cont_style.borders = borders
        return head_style, cont_style

    def get_sheet(_book, _index):
        """
        创建sheet页
        :param _book:
        :param _index:
        :return:
        """
        _name = "sheet_{}".format(str(_index))
        _sheet = _book.add_sheet(_name)
        return _sheet

    def write_head(_head, _sheet, _head_style):
        """
        写入表头
        :param _head:
        :param _sheet:
        :param _head_style:
        :return:
        """
        for head in range(len(header)):
            context = str(header[head])
            # need_width = (1 + len(context)) * 256
            need_width = 20 * 256
            table_sheet.col(head).width = need_width
            table_sheet.write(0, head, context, style=_head_style)

    # 是否插入序号
    if need_order:
        header.insert(0, '序号')

    sheet_index = 1
    book = xlwt.Workbook(encoding='utf-8')          # 创建 Excel 文件
    table_sheet = get_sheet(book, sheet_index)      # 添加sheet表
    h_style, c_style = set_style()
    write_head(header, table_sheet, h_style)

    if data and data_col:
        # 插入数据
        row = 1
        for item in data:
            if need_order:
                table_sheet.write(row, 0, row, style=c_style)  # 写入序号
                for col in range(len(header[1:])):
                    table_sheet.write(
                        row, col+1, str(item.get(data_col[col], '-') if item.get(data_col[col]) else '-'), style=c_style)
            else:
                for col in range(len(header)):
                    table_sheet.write(
                        row, col, str(item.get(data_col[col], '-') if item.get(data_col[col]) else '-'), style=c_style)
            row += 1

            if row > 50000:  # 单表数量超过 65535 条 添加新的表
                row = 1
                sheet_index += 1
                table_sheet = get_sheet(book, sheet_index)  # 添加sheet表
                write_head(header, table_sheet, h_style)
    # print(help(table_sheet))
    # table_sheet.data_validation("A1", {'validate': 'list', 'source': [1, 2, 3, 4]})
    sio = BytesIO()     # 返回文件流到浏览端下载,浏览端必须以form提交方式方能下载成功!
    book.save(sio)      # 这点很重要,传给save函数的不是保存文件名,而是一个StringIO流
    sio.seek(0)         # 保存流
    # 组装header
    headers = {"content-type": "application/vnd.ms-excel",
               "content-disposition": f'attachment;filename={quote(file_name,"utf8")}.xlsx'}
    # 以流的形式返回浏览器
    return StreamingResponse(sio, media_type='xls/xlsx', headers=headers)

【注意】安装依赖:

pip install fastapi[all] xlwt


举报

相关推荐

0 条评论