bottle直接输出文件下载及动态生成excel下载的实现

652 阅读3分钟

直接下载文件

#/usr/bin/env python
#coding=utf-8

#测试,get_static_file可下载大文件,
# headers['Content-Length']='null'#大文件必须用null

#--------------注意引用父目录的文件方法
import sys,os
currentdir = os.path.dirname(os.path.realpath(__file__))
parentdir = os.path.dirname(currentdir)
sys.path.append(parentdir)
#--------------注意引用父目录
#import bottle
from bottle import route, run,HTTPError,mimetypes,request,HTTPResponse,BottleException,parse_date,parse_range_header,_file_iter_range
from bottle import template,default_app,response
import time

#定义下载路径
download_path = './download'


def get_static_file(filename, root, mimetype='auto', download=False, charset='UTF-8'):
    """ 大文件分段下载
    """
    print('start')
    f_rd_size=204800  #每次发送数据长度
    root = os.path.abspath(root) + os.sep
    filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
    headers = dict()

    if not filename.startswith(root):
        return HTTPError(403, "Access denied.")
    if not os.path.exists(filename) or not os.path.isfile(filename):
        return HTTPError(404, "File does not exist.")
    if not os.access(filename, os.R_OK):
        return HTTPError(403, "You do not have permission to access this file.")

    if mimetype == 'auto':
        mimetype, encoding = mimetypes.guess_type(filename)
        if encoding: headers['Content-Encoding'] = encoding

    if mimetype:
        if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype:
            mimetype += '; charset=%s' % charset
        headers['Content-Type'] = mimetype
        

    if download:
        download = os.path.basename(filename if download == True else download)
        headers['Content-Disposition'] = 'attachment; filename="%s"' % download
        

    stats = os.stat(filename)
    # headers['Content-Length'] = clen = stats.st_size
    clen = stats.st_size
    headers['Content-Length']='null'#大文件必须用null
    #response.set_header('Content-Length','null')
    # lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
    # headers['Last-Modified'] = lm
    

    ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
    if ims:
        ims = parse_date(ims.split(";")[0].strip())
    if ims is not None and ims >= int(stats.st_mtime):
        headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
        return HTTPResponse(status=304, **headers)

    #body = '' if request.method == 'HEAD' else open(filename, 'rb')
    if request.method == 'HEAD':
        body = ''
        headers["Accept-Ranges"] = "bytes"
    ranges = request.environ.get('HTTP_RANGE')
    #如果断点续传
    if 'HTTP_RANGE' in request.environ:
        body=open(filename, 'rb')
        ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen))
        if not ranges:
            return HTTPError(416, "Requested Range Not Satisfiable")
        offset, end = ranges[0]
        headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen)
        headers["Content-Length"] = str(end-offset)
        if body: body = _file_iter_range(body, offset, end-offset)
        return HTTPResponse(body, status=206, **headers)
    for (k,v) in headers.items():
        response.set_header(k,v)
    with open(filename, 'rb') as f:
        while True:
            part=f.read(f_rd_size)
            print(len(part))
            if part:
                yield part
            else:
                break

#强制文件下载
@route('/download/<filename:path>')
def download(filename):
    #return static_file(filename, root=download_path, download=filename)#
    return get_static_file(filename, root=download_path, download=filename)
'''
static_file()方法,只不过是我们多添加了个参数:download=filename,
这是告诉程序,我可强制下载这个文件,如果不用这个参数,而且你的文件又是html的话,一点击这个文件,可能就在浏览器显示出来,而不是下载下来
'''
@route('/')
def index_old():
        return template('index')
#或
@route('/')
def index():
        return '''
        <html>
            <head>
            </head>
            <body>
                </p>
                <a href="/download/dnSpy_x64_jb51.rar">点击下载</a>
            </body>
        </html>
        '''
        #linuxyw.png

if __name__=="__main__":
    run(app=default_app(), host='0.0.0.0', port=8080, debug=True,reloader=True)

动态生成excel并下载

def callback():
    """
    获取列表数据
    """
    # 实例化phcodes表操作类CurrFlowLogic
    _phcodes = base_logic_codes.Codes_Logic()
    sqlstr='SELECT A.id, A.telcode as 预选号, A.isowner as 是否已选, B.orgname as 单位, A.uname as 姓名, A.uphone as 联系方式, A.slttm as 预选时间, C.packagename as 套餐,D.jtbmc as 集团包 \
	FROM public.tbl_phonecodes A left join tbl_org B on A.orgid=B.id left join tbl_package C on A.packageid=C.id left join tbl_jtb D on A.jtbid=D.id where A.id<>147 order by A.id'
    result = _phcodes.select(sqlstr)
    df=pd.DataFrame(result)
    #print(df)
    tm=datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')
    fname='选号记录导出'+tm+'.xlsx'
    utf_filename=quote(fname.encode('utf-8'))#防止不同浏览器乱码
    response.set_header('Content-Type','application/octet-stream;charset=utf-8')
    #response.add_header('Content-Type','text/html;charset=utf-8')
    response.set_header('Content-Disposition','attachment;filename="{}"'.format(utf_filename))
    #response.set_header('Transfer-Encoding','chunked')
    
    
    fpath=NamedTemporaryFile()#(mode='w+t',delete=True)#,encoding='utf-8-sig'
    #print(fpath.name)

    #bytes_data_io = io.BytesIO()
    #fpath=io.StringIO()#io.BytesIO()#'e:/tt.csv'openpyxl
    
    #-----------csv格式文件,注意修改上面的扩展名
    #df.to_csv(fpath, sep=',',header=True,na_rep='',line_terminator='\n',encoding='utf-8-sig', index=False)
    #==========  line_terminator,windows下默认是‘\r\n’会有空行。encoding='utf-8-sig'才不会乱码
    
    #------------ xlsx格式文件
    df['预选时间']= pd.to_datetime(df['预选时间'].values, utc=None,format='%Y-%m-%d %H:%M:%S', unit='s')# csv 格式不用此转换
    df.to_excel(fpath,sheet_name='Sheet1',engine='xlsxwriter', header=True,freeze_panes=(1,3),na_rep='',encoding='utf-8-sig', index=False)
    #==========  
    fpath.seek(0)
    while True:
        chunk = fpath.read(20*1024)#*1024
        #print(len(chunk))
        if not chunk:
            fpath.close()
            break
        yield chunk