使用 cherrypy 下载 docxtpl 生成的文件

Download docxtpl generated file with cherrypy

我正在使用 docxtpl 生成一个 word 文档,想知道用户如何在使用 cherrypy 生成后下载此文件,请参阅下面的代码。

我能想出的唯一解决办法是将它保存到 www 文件夹并创建一个 link 到该位置,但我相信这可以简化。

import os, os.path
import random
import string
import cherrypy
from docxtpl import DocxTemplate
import sys
from auth import AuthController, require, member_of, name_is
import socket

reload(sys)  
sys.setdefaultencoding('utf8')
cherrypy.config.update({'server.socket_port': 8000})
cherrypy.server.socket_host = '0.0.0.0'
cherrypy.engine.restart()

class Root(object):

    _cp_config = {
        'tools.sessions.on': True,
        'tools.auth.on': True    }

    @cherrypy.expose()
    def default(self, **kwargs):
        print kwargs    
        if kwargs.get('csa_no'):
#             print kwargs.get('csa_no')
            tpl=DocxTemplate('csa_tpl.docx')            
            sd = tpl.new_subdoc()
            p = sd.add_paragraph('This 1st insert')
            sd2 = tpl.new_subdoc()
            p = sd2.add_paragraph('This 2nd insert')
            context1 = {
                    'date': 'jkh',
                    'company_name' : 'Test Footer',
                    'cost' : '10,000',
                    'project_description': kwargs['project_description'],
                    'site': kwargs['site'],
                    'sp': kwargs['sp'],
                    'wo': kwargs['WO'],
                    'contract_manager': kwargs['contract_manager'],
                    'csa_no': kwargs['csa_no'],
                    'variation_reason': kwargs['variation_reason'],
                    'variation_scope_of_works': kwargs['variation_scope_of_works'],
    
                    'Initial_Contract_Value': '0,000',
                    'variation_total': ',000',
                    'Ammended_Contract_Value': '0,000',
                    'project_manager': kwargs['project_manager'],
                    'construction_manager': 'Dane Wilson',
                    'date': '30/04/2016',    
                }
            tpl.render(context1)
            file_name_with_path = '/var/www/html/csa_documents/' + kwargs['sp'] + '-'+ kwargs['WO'] + '_' + kwargs['site'] + '_-_' + 'CSA' + kwargs['csa_no'] +'.docx'
            file_name = kwargs['sp'] + '-'+ kwargs['WO'] + '_' + kwargs['site'] + '_-_' + 'CSA' + kwargs['csa_no'] +'.docx'
            print file_name
            print file_name_with_path
            tpl.save(file_name_with_path)
            return '''
            
            <!DOCTYPE html>
            <html>
            <head>
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=9" />
            <link href="//ajax.googleapis.com/ajax/libs/jquerymobile/1.4.2/jquery.mobile.min.css" rel="stylesheet">
            <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
            <script src="//ajax.googleapis.com/ajax/libs/jquerymobile/1.4.2/jquery.mobile.min.js"></script>
            <title>Broadspectrum</title>
            </head>
            <body>
            <div data-role="header" data-theme="b">
            <h1>TCSS Gateway</h1>
            </div>
            <h2>Success</h2>
            <a href="http://192.168.1.7">another submission</a>
            <a href="http://192.168.1.7/csa_documents/%s">Download & Review CSA Document</a>
            </body>

            ''' % file_name

简短的回答是,基本上你需要写一些 in-memory 流(例如 BytesIO),pre-set 一些 HTTP headers 和 return file_generator。 您的问题与一个月前的问题几乎相同,here 是我的回答。

我在 python3 中为您起草了一个小片段:

from io import BytesIO

import cherrypy
from cherrypy.lib import file_generator

from docxtpl import DocxTemplate


class GenerateDocx:
    @cherrypy.expose
    def build_docx(self, *args, **kwargs):
        iostream = BytesIO()

        tpl = DocxTemplate('csa_tpl.docx')
        ...
        # build your tpl here
        ...
        tpl.get_docx().save(iostream)

        cherrypy.response.headers['Content-Type'] = (
            # set the correct MIME type for docx
            'application/vnd.openxmlformats-officedocument'
            '.wordprocessingml.document'
        )
        cherrypy.response.headers['Content-Disposition'] = (
            'attachment; filename={fname}.docx'.format(
                fname='put your file name here'
            )
        )

        iostream.seek(0)
        return file_generator(iostream)

UPD:我刚刚检查过 the response body gets automatically wrapped with file_generator if the return value of a handler has read() method support