ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Qt实现HTTP文件上传下载(Flask服务端)

2021-08-07 18:31:28  阅读:384  来源: 互联网

标签:__ HTTP Qt Flask 上传下载 filename file qDebug os


接上一篇 Qt HTTP 基本操作:https://blog.csdn.net/gongjianbo1992/article/details/97568863

1.准备服务端测试代码

只需要上传和下载两个接口,实际应用时可能还需要 token 验证之类的,而且也没有对文件传输结果进行校验。

#using flask 2.0.1
import os,sys
from flask import Flask,request,jsonify,send_file,send_from_directory

app = Flask(__name__)
filename_temp = ''
BASE_PATH=os.path.join(os.path.dirname(os.path.abspath(__file__)),'upload')

#测试
@app.route('/',methods=['GET','POST'])
def hello():
    return '<p>Hello!</p>'

#上传
@app.route('/upload',methods=['POST'])
def upload_file():
    try:
        global filename_temp
        f = request.files['myfile']
        filename_temp = f.filename
        print('upload file:'+f.filename)
        f.save(os.path.join(BASE_PATH,f.filename))
        return jsonify({
            'filename':f.filename,
            'fileid':0 #假装对每个文件返回一个id,然后通过id再下载
        })
    except Exception as e:
        print('error:'+str(e))
        return jsonify({'error':0}),0

#下载
@app.route('/download/<fileid>',methods=['GET'])
def download_file(fileid):
    try:
        global filename_temp
        print('download file:'+filename_temp) #假装是通过id从数据库拿到的文件
        return send_from_directory(BASE_PATH,filename_temp,as_attachment=True)
    except Exception as e:
        print('error:'+str(e))
        return jsonify({'error':0}),0 

if __name__ == '__main__':
    print('server runing... ...')
    if not os.path.exists(BASE_PATH): 
        os.makedirs(BASE_PATH) 
    app.run(host='127.0.0.1',port=12345,debug=True)

2.文件上传

传文件主要是借助 QHttpMultiPart 类,并设置 Content-Type 为 multipart/form-data。我用 Flask 测试的时候,要给 QHttpPart 设置 multipart/form-data ,Flask 的 request.files 才能拿到 form-data 的 filename 等信息。

form-data 里的 name 设置为某个值如 "myfile" 后,Flask 里可以用 request.files['myfile'] 获取到这个 part,进而拿到设置的 filename。

注意 get/post 返回的 QNetworkReply 需要自己释放,Qt 5.14 可以设置 QNetworkAccessManager 的 setAutoDeleteReplies(true) 自动释放,Qt 5.15 又新增了 setTransferTimeout 设置超时时间。

有些情况还需要设置 QHttpMultiPart 的 boundary,不过我这个测试 demo 暂时用不到。

void HttpManager::upload(const QString &url, const QString &filepath)
{
    qDebug()<<"[upload file]"<<url<<QFileInfo(filepath).fileName();
    QFile *file=new QFile(filepath);
    if(!file->open(QIODevice::ReadOnly)){
        file->deleteLater();
        qDebug()<<"open file error";
        return;
    }

    QNetworkRequest request;
    request.setUrl(QUrl(url));
    //request.setRawHeader("Content-Type","multipart/form-data");

    //QHttpMultiPart需要在请求完成后释放
    QHttpMultiPart *multi_part = new QHttpMultiPart(QHttpMultiPart::FormDataType);
    QHttpPart file_part;
    file_part.setHeader(QNetworkRequest::ContentDispositionHeader,
                        QVariant(QString("form-data; name=\"myfile\"; filename=\"%1\";")
                                 .arg(QFileInfo(filepath).fileName())));
    //part.header加上这句flask.request.files才能拿到form-data的信息
    //注意不是request的header
    file_part.setRawHeader("Content-Type", "multipart/form-data");
    file_part.setBodyDevice(file);
    file->setParent(multi_part);
    multi_part->append(file_part);

    QNetworkReply *reply = manager.post(request,multi_part);
    multi_part->setParent(reply); //在删除reply时一并释放

    //因为是测试所以同步等待
    QEventLoop eventLoop;
    //上传进度
    connect(reply, &QNetworkReply::uploadProgress,
            this, [this](qint64 bytesSent, qint64 bytesTotal){
        qDebug()<<"[upload file] bytesSend"<<bytesSent<<"bytesTotal"<<bytesTotal;
    });
    //结束退出事件循环
    connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
    eventLoop.exec();

    int status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    qDebug()<<"reply"<<status_code<<QString(reply->readAll());
    qDebug()<<"[upload file] finished";
}

3.文件下载

文件的下载相对就更简单的,只是要注意处理下载的异常情况。

void HttpManager::download(const QString &url, const QString &fileid, const QString &filepath)
{
    qDebug()<<"[download file]"<<url<<fileid;

    QNetworkRequest request;
    request.setUrl(QUrl(url+QString("/%1").arg(fileid)));
    QNetworkReply *reply = manager.get(request);

    //先删除已有的
    QFile file(filepath);
    file.remove();

    //因为是测试所以同步等待
    QEventLoop eventLoop;
    //数据可读
    connect(reply, &QNetworkReply::readyRead, this, [this,reply,&file](){
           if(!reply->isOpen()){
               if(!reply->open(QIODevice::ReadOnly)){
                   qDebug()<<"[download file] reply open failed";
                   return;
               }
           }
           if(!file.isOpen()){
               if(!file.open(QIODevice::WriteOnly | QIODevice::Append)){
                   qDebug()<<"[download file] file open failed";
                   return;
               }
           }
           file.write(reply->readAll());
    });
    //下载进度
    connect(reply, &QNetworkReply::downloadProgress,
            this, [this](qint64 bytesReceived, qint64 bytesTotal){
        qDebug()<<"[download file] bytesReceived"<<bytesReceived<<"bytesTotal"<<bytesTotal;
    });
    //结束退出事件循环
    connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
    eventLoop.exec();
    file.close(); //关闭文件

    int status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    //如果是无效的响应吧数据清除
    if(status_code != 200)
        file.remove();
    qDebug()<<"reply"<<status_code<<QString(reply->readAll());
    qDebug()<<"[download file] finished";
}

4.操作实例(代码链接)

我的 demo 只是测试了两个功能的基本使用,很多异常场景需要自己根据需求处理。

我的示例链接:https://github.com/gongjianbo/MyTestCode2021/tree/master/Qt/TestQt_20210807_HttpFile

5.参考

文档:https://doc.qt.io/qt-5/qnetworkaccessmanager.html

博客:https://blog.csdn.net/u012321968/article/details/111636380

标签:__,HTTP,Qt,Flask,上传下载,filename,file,qDebug,os
来源: https://blog.csdn.net/gongjianbo1992/article/details/119490369

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有