使用 NetworkManager 上传文件不起作用

Posted

技术标签:

【中文标题】使用 NetworkManager 上传文件不起作用【英文标题】:Upload files using QNetworkManager is not woking 【发布时间】:2021-05-15 23:16:10 【问题描述】:

所以我正在尝试使用 Qt Network Manager 将一个简单的文本文件上传到我正在服务的 php 脚本中。但它不起作用。我尝试了使用 QHttpMultiPart 和在请求中设置原始数据标头的示例,但没有任何效果。

这是我的 Qt 代码:

#include <QCoreApplication>

#include <QNetworkReply>
#include <QNetworkRequest>
#include <QDebug>
#include <QEventLoop>
#include <QObject>
#include <QVariantMap>
#include <QJsonDocument>
#include <QFile>
#include <QHttpMultiPart>

int main(int argc, char *argv[])

    QCoreApplication a(argc, argv);

    QString address = "http://localhost/api_test/";
    //address = "https://dashboard.viewmind.ai/dashboard/api_test/welcome.php";

    QUrl api_url = QUrl(address);

    QVariantMap postDatamap;
    postDatamap.insert("Name","Ariel Ñoño");
    postDatamap.insert("Age",37);

    QJsonDocument json = QJsonDocument::fromVariant(postDatamap);

    qDebug() << "Sending the request";
    QNetworkAccessManager *networkManager = new QNetworkAccessManager();
    QNetworkRequest request(api_url);


    QString bound = "<<<<<boundary>>>>>";
    request.setRawHeader(QString("Content-Type").toUtf8(),QString("multipart/form-postData; boundary=" + bound).toUtf8());


    //QByteArray postData;
    QByteArray postData(QString("--" + bound + "\r\n").toUtf8());
    postData.append("Content-Disposition: form-postData; name=\"action\"\r\n\r\n");
    postData.append("welcome.php\r\n");
    postData.append(QString("--" + bound + "\r\n").toUtf8());
    postData.append("Content-Disposition: form-postData; name=\"uploaded\"; filename=\"");
    postData.append("test.json");
    postData.append("\"\r\n");
    postData.append("Content-Type: text/xml\r\n\r\n"); //postData type

    QFile file("test.json");
        if (!file.open(QIODevice::ReadOnly))
            qDebug() << "QFile Error: File not found!";
            delete networkManager;
            return 0;
         else  qDebug() << "File found, proceed as planned"; 

    postData.append(file.readAll());
    postData.append("\r\n");
    postData.append(QString("--" + bound + "\r\n").toUtf8());

    request.setRawHeader(QString("Content-Length").toUtf8(), QString::number(postData.length()).toUtf8());


    //request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json; charset=utf-8");
    //request.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-postData; name=\"text\""));
    //qDebug() << QString(json.toJson());

    //QHttpMultiPart multipart(QHttpMultiPart::FormDataType);
    //QHttpPart textPart;

//    QFile file("test.json");
//    if (!file.open(QIODevice::ReadOnly))
//        qDebug() << "Could not open file for reading";
//        delete networkManager;
//        return 0;
//    
    //textPart.setBodyDevice(&file);
    //multipart.append(textPart);
    //file.setParent(&multipart);

    //QNetworkReply *reply = networkManager->post(request,json.toJson());
    //QNetworkReply *reply = networkManager->post(request,file.readAll());
    //QNetworkReply *reply = networkManager->post(request,&multipart);
    QNetworkReply *reply = networkManager->post(request,postData);
    //file.setParent(reply);
    //multipart.setParent(reply);

    // Using the loop to wait for the reply to finish.
    QEventLoop loop;
    QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    qDebug() << "Reply is finished";

    //file.close();

    if (reply->error() != QNetworkReply::NoError)
        qDebug() << "The following error ocurred";
        qDebug() << reply->errorString();
        return 0;
    

    QString postData_returned = QString::fromUtf8(reply->readAll());

    qDebug() << "DATA RETURNED";
    qDebug() << postData_returned;

    return 0;

我的 php 代码是这样的

<?php
header("Access-Control-Allow-Origin: *");  // Anyone can access
header("Content-Type: application/json; charset=UTF-8"); // Will return json data. 

error_log("FILES iS");
vardump_toerror($_FILES);

?>

我的理解是 $_FILES 超级全局应该填充文件信息。我弄错了吗?但是打印出来的显示是空的。

【问题讨论】:

【参考方案1】:

我不是 PHP 专家,但我发现没有必要使用内容类型 application/json,因为 multipart(提交表单)不是该协议的一部分。另一方面,我找不到vardump_toerror 函数的引用,所以我用var_dump 更改所以我的测试php 是:

<?php
  var_dump($_FILES);
?>

在 PyQt5 的 previous question 中,我为 django 实现了类似的逻辑,该逻辑也适用于这种情况,因此我将显示翻译。

#include <QCoreApplication>
#include <QFile>
#include <QHttpMultiPart>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTextCodec>

QHttpMultiPart *buildMultpart(const QVariantMap & data, const QMap<QString, QString> filenames)

    QHttpMultiPart *multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
    QVariantMap::const_iterator i_data = data.constBegin();
    while (i_data != data.constEnd()) 
        QHttpPart postpart;
        postpart.setHeader(QNetworkRequest::ContentDispositionHeader, QString("form-data; name=\"%1\"").arg(i_data.key()));
        postpart.setBody(i_data.value().toByteArray());
        multipart->append(postpart);
        ++i_data;
    
    QMap<QString, QString>::const_iterator i_filenames = filenames.constBegin();
    while (i_filenames != filenames.constEnd()) 

        QFile *file = new QFile(i_filenames.value());
        if(!file->open(QIODevice::ReadOnly))
            delete  file;
            continue;
        
        QHttpPart postpart;
        postpart.setHeader(QNetworkRequest::ContentDispositionHeader,
                           QString("form-data; name=\"%1\"; filename=\"%2\"")
                           .arg(i_filenames.key(), file->fileName()));
        postpart.setBodyDevice(file);
        multipart->append(postpart);
        file->setParent(multipart);
        ++i_filenames;
    
    return multipart;



int main(int argc, char *argv[])

    QCoreApplication a(argc, argv);
    QUrl url("http://localhost:4000/upload.php");
    QNetworkAccessManager manager;
    QMap<QString, QString> filenames;
    filenames["fileToUpload"] = "/path/of/data.txt";
    QHttpMultiPart *multipart = buildMultpart(, filenames);
    QNetworkRequest request(url);
    QNetworkReply *reply = manager.post(request, multipart);
    multipart->setParent(reply);
    QObject::connect(reply, &QNetworkReply::finished, QCoreApplication::quit);
    a.exec();
    if(reply->error() == QNetworkReply::NoError)
        qDebug() << reply->readAll();
    
    else
        qDebug() << reply->error() << reply->errorString();
    
    delete reply;

    return 0;

输出:

"array(1) \n  [\"fileToUpload\"]=>\n  array(5) \n    [\"name\"]=>\n    string(8) \"data.txt\"\n    [\"type\"]=>\n    string(0) \"\"\n    [\"tmp_name\"]=>\n    string(14) \"/tmp/phpVmOAhO\"\n    [\"error\"]=>\n    int(0)\n    [\"size\"]=>\n    int(6)\n  \n\n"

【讨论】:

你是男人!!!像魅力一样工作。抱歉 vardump_toerror。它只是一个将变量转储到字符串并将变量转储到错误日志的函数。我使用了您的代码原样并且它有效。我要弄清楚我和我的区别是什么。谢谢!!!

以上是关于使用 NetworkManager 上传文件不起作用的主要内容,如果未能解决你的问题,请参考以下文章

我的Android进阶之旅------&gt;Android中ListView中嵌套(ListView)控件时item的点击事件不起作的问题解决方法

如何在用户离线时尝试将数据添加/上传到 Firebase 时显示错误?

如何禁用NetworkManager

JQuery Flickr文件上传不起作用

使用 Multer 重命名上传的文件不起作用 (Express.js)

使用具有角度7平均堆栈的multer中间件上传文件不起作用