【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
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是使用POI生成Excel表格并返回流给前端的Java代码示例: ```java @RestController public class ExcelController { @GetMapping("/download/excel") public ResponseEntity<InputStreamResource> downloadExcel() throws IOException { // 创建Excel文档 Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("Sheet1"); // 创建表头 Row headerRow = sheet.createRow(0); headerRow.createCell(0).setCellValue("姓名"); headerRow.createCell(1).setCellValue("年龄"); // 填充数据 Row dataRow1 = sheet.createRow(1); dataRow1.createCell(0).setCellValue("张三"); dataRow1.createCell(1).setCellValue(20); Row dataRow2 = sheet.createRow(2); dataRow2.createCell(0).setCellValue("李四"); dataRow2.createCell(1).setCellValue(25); // 将Excel文档写入流 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); workbook.write(outputStream); ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray()); // 设置响应头 HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment; filename=example.xlsx"); // 返回流给前端 return ResponseEntity.ok() .headers(headers) .contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) .body(new InputStreamResource(inputStream)); } } ``` 在以上代码中,我们使用了POI库创建了一个Excel文档,并将其写入到一个字节数组输出流中。然后,我们又创建了一个字节数组输入流,并设置了响应头,最后使用ResponseEntity将输入流返回前端。注意,我们还需要使用MediaType指定返回的数据类型为Excel表格。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神精兵院院长

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值