python-docx 导出World写进内存中封装为response

python-docx 导出Word写进内存中封装为response

因为公司需要将数据导出为word文件,恰好又是前后端分离开发,所以前端的请求需要携带Token。并不能简单的window.open() 。以及在后端处理Wold的时候用到python-docx模块写起来不是很顺手,就随便封装了一下,然后就是在return这个请求的时候将document对象转化为文件流返回等等吧。

代码如下
# -*- coding:utf-8 -*-
# 项目周报导出模块
# created by xiaofan
from io import BytesIO
from django.http import HttpResponse
from django.utils.http import urlquote
from docx import Document
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT, WD_COLOR_INDEX
from docx.shared import Pt, Cm, RGBColor  # 磅数
from docx.oxml.ns import qn  # 中文格式


class CreateTable:
    """
        table 操作
    """

    def __init__(self, rows, cols, width_list, height, document, font_size=10, font_color=(0, 0, 0), floats='LEFT',
                 font_name=u'仿宋'):
        """
        添加rows行cols列表格
        :param rows: 行
        :param cols: 列
        :param font_size: 字体大小
        :param font_color: 字体颜色
        :param floats: 居中格式
        """
        self.rows = rows
        self.cols = cols
        self.font_name = font_name
        self.width_list = width_list
        self.height = height
        self.table = document.add_table(rows=rows, cols=cols, style='Table Grid')
        self.table.style.font.size = Pt(font_size)
        self.table.style.font.name = font_name
        self.table.style.element.rPr.rFonts.set(qn('w:eastAsia'), font_name)
        self.table.style.font.color.rgb = RGBColor(*font_color)
        self.table.style.paragraph_format.alignment = getattr(WD_PARAGRAPH_ALIGNMENT, floats)
        self.table.alignment = WD_TABLE_ALIGNMENT.CENTER
        self.set_row_width()

    def set_row_width(self):
        for i in range(self.cols):
            self.table.cell(0, i).width = Cm(self.width_list[i])
        for i in range(self.rows):
            self.table.rows[i].height = Cm(self.height)

    def merge(self, tuple1, tuple2, text, font_name=u'仿宋', font_size=10, bold=False):
        self.table.cell(*tuple1).merge(self.table.cell(*tuple2))
        tr = self.table.cell(*tuple1).paragraphs[0].add_run(text)
        tr.font.name = font_name
        tr.font.size = Pt(font_size)
        tr.font.bold = bold
        xu = self.table.cell(*tuple1)
        xu.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
        xu.alignment = WD_TABLE_ALIGNMENT
        xt, y = tuple1
        self.table.rows[xt].height = Cm(self.height)

    def write_row(self, tuple1, tuple2, data_list, font_size=11, bold=False):
        LA, LB = tuple1
        RA, RB = tuple2
        if (LA == RA and RB >= LB) or (LB == RB and RA >= LA):
            if LA == RA:
                for index, i in enumerate(range(LB, RB)):
                    self._write((LA, i), data_list[index], font_size, bold)
                return
            if LB == RB:
                for index, i in enumerate(range(LA, RA)):
                    self._write((i, LB), data_list[index], font_size, bold)
                return

    def __add_row(self):
        return self.table.add_row().cells

    def add_row(self, data_list, color=None, font_size=11, bold=False):
        """
        :param bold:
        :param font_size:
        :param data_list: 数据列表
        :param color: 字体下标和颜色组成的字典 ps: {0:'RED',2:'YELLOW}
        :return:
        """
        row_cells = self.__add_row()
        for i in range(self.cols):
            xa = row_cells[i]
            xa.width = Cm(self.width_list[i])
            xa.height = Cm(self.height)
            xa.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
            xx = xa.paragraphs[0].add_run(str(data_list[i]))
            xx.font.name = self.font_name
            xx.font.size = Pt(font_size)
            xx.font.bold = bold
            if color and i in color.keys():
                xx.font.highlight_color = getattr(WD_COLOR_INDEX, color[i])

    def _write(self, tuple1, text, font_size, bold):
        xu = self.table.cell(*tuple1)
        xu.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
        xx = xu.paragraphs[0].add_run(text)
        xx.font.name = self.font_name
        xx.font.size = Pt(font_size)
        xx.font.bold = bold


#  周报导出主方法
def weekreport_download():
    def _write(xp, info, font_type, font_size, bold, underline):
        xs = xp.add_run(info)
        xs.font.name = font_type  # 设置西文字体
        xs.element.rPr.rFonts.set(qn('w:eastAsia'), font_type)  # 设置段中文字体
        xs.font.size = Pt(font_size)
        xs.font.bold = bold
        xs.underline = underline

    def continue_P(xp, infos='', font_type=u'仿宋', font_size=15.0, bold=False, underline=False, undLs=None,
                   defaultundL=(1,)):
        if not undLs:
            _write(xp, infos, font_type, font_size, bold, underline)
        else:
            for index, ev in enumerate(undLs):
                if index in [*defaultundL]:
                    _write(xp, ev, font_type, font_size, bold, True)
                else:
                    _write(xp, ev, font_type, font_size, bold, False)
        return xp

    def add_P(floats='LEFT', infos='', font_type=u'仿宋', font_size=15.0, bold=False, underline=False, jump=0, undLs=None,
              defaultundL=(1,)):
        """
        :param floats:  对齐方式
        :param infos: 写的内容
        :param font_type: 字体类型
        :param font_size: 字体大小
        :param bold: 是否加粗
        :param underline: 是否下划线
        :param jump: 在句尾加几个换行
        :param undLs: 是否是拼接字符串列表
        :param defaultundL: 列表中第几个需要加下换线
        :return:
        """
        xs = document.add_paragraph()  # 初始化建立第一个自然段
        xs.alignment = eval('WD_PARAGRAPH_ALIGNMENT.' + floats)  # 对齐方式为居中,没有这句默认为左对齐
        if not undLs:
            continue_P(xp=xs, infos=infos, font_type=font_type, font_size=font_size, bold=bold, underline=underline)
        else:
            for index, ev in enumerate(undLs):
                if index in [*defaultundL]:
                    continue_P(xp=xs, infos=ev, font_type=font_type, font_size=font_size, bold=bold, underline=True)
                else:
                    continue_P(xp=xs, infos=ev, font_type=font_type, font_size=font_size, bold=bold)

        if jump:
            [document.add_paragraph() for _ in range(jump)]
        return xs

    def add_section(direction=True, first=False):
         """
        :param direction:  纸张方向 true为竖版  false横版
        :param first: 是否是第一节
        :return:
        """
        if not first:
            document.add_section()
        header = document.sections[-1].header  # 获取第一个节的页眉
        header.is_linked_to_previous = False  # 不继承上一节
        paragraph = header.paragraphs[0]  # 获取页眉的第一个段落
        space_num = 47 if direction else 96
        run = paragraph.add_run('编号: XXXX' + ' ' * space_num + 'XXXXX周报')  # 添加页面内容
        run.font.size = Pt(10)
        run.underline = True
        section = document.sections[-1]
        if direction:
            section.page_height = Cm(29.7)
            section.page_width = Cm(21)
        else:
            section.page_width = Cm(29.7)
            section.page_height = Cm(21)

    document = Document()
    document.styles['Normal'].font.name = u'仿宋'  # 设置文档的基础字体
    document.styles['Normal'].element.rPr.rFonts.set(qn('w:eastAsia'), u'仿宋')  # 设置文档的基础中文字体

 
    add_section(first=True)
    add_P(infos='编号:2021-000', font_size=14, jump=3)

    # 建立第一个自然段
    add_P(floats='CENTER', infos='XXXXXX\n项目周报', font_size=36, bold=True, jump=1)
    add_P(floats='CENTER', infos='(2021.03.08—2021.03.14)', font_size=16, bold=True, jump=11)
    add_P(floats='CENTER', infos='2021年03月14日', font_size=22, bold=True, jump=1)

    document.add_page_break()

    # 建立第二个自然段
    add_P(floats='CENTER', infos='项目周报', font_size=22, bold=True)
    add_P(undLs=['  XX', ' XX', ' XX', ' XX', 'XXXX', ' 4.2 ', 'XX', ' XXX ', 'XXXX', ' XXX ',
                 'XXX', ' XXXX ', 'XX。'], font_size=12, bold=True, defaultundL=(1, 3, 5, 7, 9, 11))


    document.add_page_break()

    #  增加第二节
    add_section(False)

    # 插入表格
    t1 = CreateTable(1, 13, floats='CENTER',
                     width_list=[1.04, 2.64, 1.78, 5.56, 1.86, 1.40, 1.40, 1.40, 1.40, 1.40, 1.40, 0.75, 1.74],
                     height=1.14, document=document)

    t1.add_row([' ' for _ in range(13)])
    t1.merge((0, 0), (1, 0), 'XX', bold=True)
    t1.merge((0, 1), (1, 1), 'XXXXXX', bold=True)
    t1.merge((0, 2), (1, 2), 'XXXX', bold=True)
    t1.merge((0, 3), (1, 3), 'XX', bold=True)
    t1.merge((0, 4), (1, 4), 'XX', bold=True)
    t1.merge((0, 5), (0, 9), 'XX', bold=True)
    t1.merge((0, 10), (1, 10), 'XX', bold=True)
    t1.merge((0, 11), (1, 11), 'XX', bold=True)
    t1.merge((0, 12), (1, 12), 'XX', bold=True)
    t1.merge((1, 5), (1, 5), 'XX', bold=False)
    t1.merge((1, 6), (1, 6), 'XX', bold=False)
    t1.merge((1, 7), (1, 7), 'XX', bold=False)
    t1.merge((1, 8), (1, 8), 'XX', bold=False)
    t1.merge((1, 9), (1, 9), 'XX', bold=False)
    t1.add_row(
        [1, 'XX', 'XX', 'XX', '6000', '1', '500', '0', '0',
         'XX', '0', 'XX', '0.0%'], color={12: 'RED'})

	#  这里是将文件保存在内存中
	bio = BytesIO() 
    document.save(bio)
    bio.seek(0)
    response = HttpResponse(bio, content_type='application/msword')
    response['Access-Control-Expose-Headers'] = 'file_name'
    response['file_name'] = '周报汇总'
    response['Content-Disposition'] = 'attachment;filename=%s.doc' % urlquote('周报汇总')
    return response


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值