简单说明:前端通过XMLHttpRequest发起请求,后端flask使用方法send_file(file, as_attachment=True)直接返回文件

一. Layui 前端页面

1.Layui html还是常用的数据表格,代码如下

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>layui</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <link rel="stylesheet" href="../static/layui-v2.6.8/layui/css/layui.css">
</head>
<body>
<div class="layuimini-container">
    <div class="layuimini-main">

        <table class="layui-hide" id="currentTableId" lay-filter="currentTableFilter"></table>

        <script type="text/html" id="currentTableBar">
			<a class="layui-btn layui-btn-primary layui-border-red layui-btn-xs data-count-edit" lay-event="download">下载</a>
        </script>
		
		<script type="text/html" id="fileStatus">
		    {{ '{{#if (d.fileStatus == 1) { }}
		    <span style="color: green">正常</span>
		    {{# }else if(d.fileStatus == 0){ }}
		    <span style="color: red">损坏</span>
		    {{# } }}'|safe }}
		</script>


    </div>
</div>
<script src="../static/layui-v2.6.8/layui/layui.js" charset="utf-8"></script>

<script>
    layui.use(['form', 'table'], function () {
        var $ = layui.jquery,
            form = layui.form,
            table = layui.table;
		
        table.render({
            elem: '#currentTableId',
            url: '/file-query',
			method:'post',
            toolbar: '#toolbarDemo',
            defaultToolbar: [{ layEvent: 'refresh', icon: 'layui-icon-refresh'}, 'filter', 'exports', 'print'],
            cols: [[
                {type: "checkbox", width: 50},
                {field: 'id', width: 80, title: '序号', align:'center', sort: true},
				{field: 'fileID', width: 120, title: '文件ID', align:'center', sort: true},
                {field: 'fileName', width: 480, title: '文件名称', align:'center', sort: true},
				{field: 'Size', width: 180, title: '文件大小(B)',align:'center', sort: true},
                {field: 'fileSize', width: 180, title: '文件大小(KB)',align:'center', sort: true},
				{field: 'uploadTime', width: 180, title: '上传时间',align:'center', sort: true},
				{field: 'fileStatus', width: 120, title: '文件状态', align:'center', sort: true, templet: '#fileStatus',},
                {title: '操作', minWidth: 200, toolbar: '#currentTableBar', align: "center"}
            ]],
            limits: [10, 15, 20, 25, 50, 100],
            limit: 10,
            page: true,
            skin: 'row',
			even: true
			
        });

        table.on('tool(currentTableFilter)', function (obj) {
            var data = obj.data;
            if (obj.event === 'download') {		
				var xmlResquest = new XMLHttpRequest(); // 获取XMLHttpRequest
				xmlResquest.open("POST", "/file-download", true); //  发起请求 url-/file-download
				xmlResquest.setRequestHeader("Content-type", "application/json"); // 设置请求头类型
				xmlResquest.setRequestHeader("fileName",data.fileName);  //携带参数
				xmlResquest.responseType = "blob";
				
				xmlResquest.onload = function(oEvent) {//  返回
					var content = xmlResquest.response;
					var elink = document.createElement("a");// 组装a标签

					var newFileName = data.fileName;  //下载的文件名
					
					elink.download = newFileName;  //设置文件下载路径
					elink.style.display = "none";
					var blob = new Blob([content]);
 
					if(blob.size==0){ //解决下载不存在文件的问题,根据blob大小判断
						layer.msg('服务器没找到此文件,请联系管理员!');
					}else{
						elink.href = URL.createObjectURL(blob);
						document.body.appendChild(elink);
						elink.click();
						document.body.removeChild(elink);
					}
				};
				xmlResquest.send();
                

            }  
        });
		
    });
</script>

</body>
</html>

2.上面代码出来数据表格外,增加XMLHttpRequest请求

table.on('tool(currentTableFilter)', function (obj) {
            var data = obj.data;
            if (obj.event === 'download') {		
				var xmlResquest = new XMLHttpRequest(); // 获取XMLHttpRequest
				xmlResquest.open("POST", "/file-download", true); //  发起请求 url-/file-download
				xmlResquest.setRequestHeader("Content-type", "application/json"); // 设置请求头类型
				xmlResquest.setRequestHeader("fileName",data.fileName);  //携带参数
				xmlResquest.responseType = "blob";
				
				xmlResquest.onload = function(oEvent) {//  返回
					var content = xmlResquest.response;
					var elink = document.createElement("a");// 组装a标签

					var newFileName = data.fileName;  //下载的文件名
					
					elink.download = newFileName;  //设置文件下载路径
					elink.style.display = "none";
					var blob = new Blob([content]);
 
					if(blob.size==0){ //解决下载不存在文件的问题,根据blob大小判断
						layer.msg('服务器没找到此文件,请联系管理员!');
					}else{
						elink.href = URL.createObjectURL(blob);
						document.body.appendChild(elink);
						elink.click();
						document.body.removeChild(elink);
					}
				};
				xmlResquest.send();
            }  
        });

二.后端flask从ftp下载文件保存,然后发送给客户端

1.flask代码

# encoding:utf-8
"""
@file = app
@author = zouju
@create_time = 2022-06-14- 8:59
"""
import os

from flask import Flask, render_template, jsonify, request, send_file
import pymssql
from ftplib import FTP, error_perm
import socket


def ftpconnect(host, port, username, password):
    ftp = FTP()
    # ftp.set_debuglevel(2)         #打开调试级别2,显示详细信息
    # ftp.encoding = 'utf-8'  # 解决中文编码问题,默认是latin-1
    try:
        ftp.connect(host, port)  # 连接
        ftp.login(username, password)  # 登录,如果匿名登录则用空串代替即可
        print(ftp.getwelcome())  # 打印欢迎信息
    except(socket.error, socket.gaierror):  # ftp 连接错误
        print("ERROR: cannot connect [{}:{}]".format(host, port))
        return 'FTP Connect failed'
    except error_perm:  # 用户登录认证错误
        print("ERROR: user Authentication failed ")
        return 'FTP Authentication failed'
    return ftp


def downloadfile(ftp, remotepath, localpath):
    """
     下载文件
    :param ftp:
    :param remotepath:
    :param localpath:
    :return:
    """
    bufsize = 10240  # 设置缓冲块大小
    fp = open(localpath, 'wb')  # 以写模式在本地打开文件

    res = ftp.retrbinary(
        'RETR ' + remotepath,
        fp.write,
        bufsize)  # 接收服务器上文件并写入本地文件
    if res.find('226') != -1:
        res_flag = 1
        print('download file complete', localpath)
    else:
        res_flag = 0
    ftp.set_debuglevel(0)  # 关闭调试
    fp.close()  # 关闭文件
    return res_flag


def exec_sql_server(sql):
    server = '10.10.80.xxx:1433'
    database = 'database'
    user = 'sa'
    password = 'password'
    try:
        print(f'查询SQL:{sql}')
        db = pymssql.connect(server, user, password, database, charset='utf8')  # 连接到sql server数据库 charset='utf8'
        cur = db.cursor()  # 获取该数据库连接下的环境变量
        cur.execute(sql)  # 执行语句

    except Exception as e:
        print(e)

    else:
        result = cur.fetchall()  # get result
        db.commit()  # 一定要commit,pymssql包默认是需要手动commit的,否则事务不生效
        db.close()  # 关闭数据库连接
        return result


app = Flask(__name__)


@app.get('/')
def index():
    return render_template('layui-download.html')


@app.post('/file-query')
def file_query():
    count = 0
    data_list = []
    page_number = int(request.form.get('page', ''))
    page_limit = int(request.form.get('limit', ''))
    start_index = (page_number - 1) * page_limit
    end_index = page_number * page_limit
    res = exec_sql_server(f'select * from file_info where id > {start_index} and id < {end_index} order by id desc')
    num = exec_sql_server('select count(*) from file_info')
    for item in res:
        count += 1
        item_size = int(item[2] / 1024) + 1
        item_time = str(item[3])[:19]
        item_data = {
            "id": count,
            "fileID": item[0],
            "fileName": item[1],
            "Size": item[2],
            "fileSize": item_size,
            "uploadTime": item_time,
            "fileStatus": item[4],
        }
        data_list.append(item_data)

    return jsonify({"code": 0, "msg": "", "count": num, "data": data_list})


@app.post('/file-download')
def file_download():
    post_header = request.headers  # 获取request header
    file_name = post_header['fileName']
    print('fileName:', file_name)
    download_path = r'D:\python\2022\layui\layui-flask\static\download'
    des_file = fr'{download_path}\{file_name}'

    # 判断文件夹是否存在
    if not os.path.exists(download_path):
        os.makedirs(download_path)
    # 判断文件是否存在
    if os.path.exists(des_file):
        return send_file(des_file, as_attachment=True)
    else:
        FTP_HOST = '183.129.xxx.xxx'
        FTP_PORT = 21
        FTP_USERNAME = 'username'
        FTP_PASSWORD = 'password'
        ftp = ftpconnect(FTP_HOST, FTP_PORT, FTP_USERNAME, FTP_PASSWORD)

        if ftp not in ('FTP Connect failed', 'FTP Authentication failed'):
            ftp.voidcmd('TYPE I')
            downloadfile(ftp, file_name, des_file)
            return send_file(des_file, as_attachment=True)
        else:
            return jsonify({"code": 0, "msg": "ftp connect failed"})


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

三.相关截图

1.文件列表

2.点击'下载'

 

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐