Uploading a file in Multipart form data in Qt5 - qt

I am trying desperately to upload a file to a server in Multipart. I have almost the same code as in the Qt docs, but the file isn't uploading to the server.
Here's what I have in my Debug:
---------Uploaded-------------- 3672 of 3672
---------Uploaded-------------- 3672 of 3672
---------Uploaded-------------- 3672 of 3672
---------Uploaded-------------- 0 of 0
----------Finished--------------
"Error transferring http://MyUrlHere.com/uploadFile - server replied: Bad Request" 400 QNetworkReplyHttpImpl(0x17589ff0)
The problem is not coming from the server because when I try uploading a file on it in multipart with a Chrome or Firefox extention it actually works!
Here is my code:
QUrl testUrl("http://MyUrlHere.com/uploadFile ");
QNetworkRequest request(testUrl);
QNetworkProxy proxy;
proxy.setType(QNetworkProxy::HttpProxy);
proxy.setHostName("proxy");
proxy.setPort(8080);
QNetworkProxy::setApplicationProxy(proxy);
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QString preview_path = "C:/Users/Desktop/image.jpg";
QHttpPart previewPathPart;
previewPathPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"preview_path\""));
previewPathPart.setBody(preview_path.toLatin1());
QString preview_name = "image.jpg";
QHttpPart previewFilePart;
previewFilePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
previewFilePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"preview_file\"; filename=\""+ preview_name + "\""));
QFile *file = new QFile(preview_path);
file->open(QIODevice::ReadOnly);
previewFilePart.setBodyDevice(file);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(previewPathPart);
multiPart->append(previewFilePart);
QNetworkAccessManager *networkManager= new QNetworkAccessManager;
reply = networkManager->post(request, multiPart);
multiPart->setParent(reply); // delete the multiPart with the reply
connect(reply, SIGNAL(finished()),
this, SLOT (uploadDone()));
connect(reply, SIGNAL(uploadProgress(qint64, qint64)),
this, SLOT (uploadProgress(qint64, qint64)));
}
void ApkDialog::uploadProgress(qint64 bytesSent, qint64 bytesTotal) {
qDebug() << "---------Uploaded--------------" << bytesSent<< "of" <<bytesTotal;
}
void ApkDialog::uploadDone() {
qDebug() << "----------Finished--------------" << reply->errorString() <<reply->attribute( QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug()<<reply;
// reply->deleteLater();
}

I found the error. It was a request error. there is a little thing missing in Qt docs.
Here is my code that is working :
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart imagePart;
//imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"version.txt\""));/* version.tkt is the name on my Disk of the file that I want to upload */
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"name\""));
textPart.setBody("toto");/* toto is the name I give to my file in the server */
QString apkLocation = apktextEdit->text();
QFile *file = new QFile(apkLocation);
file->open(QIODevice::ReadOnly);
imagePart.setBodyDevice(file);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(textPart);
multiPart->append(imagePart);
QUrl url("http://MyUrl.com");
QNetworkRequest request(url);
QNetworkAccessManager *networkManager= new QNetworkAccessManager;
reply = networkManager->post(request, multiPart);
multiPart->setParent(reply); // delete the multiPart with the reply
connect(reply, SIGNAL(finished()),
this, SLOT (uploadDone()));
connect(reply, SIGNAL(uploadProgress(qint64, qint64)),
this, SLOT (uploadProgress(qint64, qint64)));
}

There is no need in filename=\"version.txt\" here:
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"file\"; filename=\"version.txt\""));
You set file name here:
QString apkLocation = apktextEdit->text();
And it works fine just with the following line:
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"file\""));
Anyway thanks a lot, it was really heplful!

Related

QNetworkReply::RemoteHostClosedError

I was learning the Github API and wanted to create an Issue on github by Qt.
But, when I submit my Issue, the "Debug Console" said
QNetworkReply::RemoteHostClosedError
(I catched error).
I don't know why, please help me!
Code on Github:
void MainWindow::on_pushButton_2_clicked()
{
QString id = ui->ID_Send->text();
QJsonObject JJ;
QJsonDocument jd;
JJ["title"] = ui->Title->text();
JJ["body"] = ui->Comment->toPlainText();
QHttpMultiPart *part = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart info;
info.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/json"));
jd.setObject(JJ);
info.setBody(jd.toJson());
part->append(info);
QNetworkRequest request;
qDebug()<<"https://api.github.com/repos/MoyuSteve/Comment/issues?client_id="+
client_id+"&client_secret="+client_secret;
request.setUrl(QUrl("https://api.github.com/repos/MoyuSteve/Comment/issues?client_id="+
client_id+"&client_secret="+client_secret));
QNetworkReply* reply = manager->post(request,part);
QObject::connect(reply, &QNetworkReply::finished, [reply, this] () {
if (reply->error() == QNetworkReply::NoError) {
ui->status->setText("ok");
} else {
ui->status->setText("failed");
qDebug()<<reply->error();
}
reply->deleteLater();
});
}
You can take example in this answer or here.
Compared to your code, it sets the header to "application/json".
And it uses a QNetworkAccessManager to send network requests and receive replies.
QUrl url("https://api.github.com/repos/" + owner +"/" + project + "/tags");
qInfo() << url.toString();
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkAccessManager nam;
QNetworkReply * reply = nam.get(request);

NetworkReply HttpStatusCode QVariant(Invalid)

I'm using the code below to make a http request:
QNetworkReply* ApiRequest::req(QString url, QString method, QByteArray data) {
QByteArray request_method = method.toUtf8();
QNetworkAccessManager* manager = new QNetworkAccessManager();
QNetworkRequest request("http://127.0.0.1:9090" + url);
request.setRawHeader("Content-Type", "application/json");
QNetworkReply* reply = manager->sendCustomRequest(request, request_method, data);
return reply;
}
void ApiRequest::requestConfig()
{
NetworkReply* reply = req("/configs",
"GET",
"");
}
The Remote server did execute the request and reply a 204 code.
I have used wireshark to capture and make sure it had reply a 204 No Content.
However, the output there is QVariant(Invalid), the toInt output is 0.
I tried to change PUT to GET but it still not working.
You are analyzing the state even when the request has not been made so it is valid that the result is null, what you should do is analyze it when the finished signal is emitted:
QNetworkReply* reply = mg->sendCustomRequest(request, "PUT", "....some json....");
connec(reply, &QNetworkReply::finished, [reply](){
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
});
Update:
connec(mg, &QNetworkAccessManager::finished, [](QNetworkReply *reply){
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
});
Update 2:
Is there a way to return that reply when it is finished?
Yes, use a QEventLoop, but in general it is a bad design since you should use signals and slots to notify you of the changes.
QNetworkReply* ApiRequest::req(const QString & url, const QString & method, const QByteArray & data) {
QByteArray request_method = method.toUtf8();
QNetworkAccessManager manager;
QNetworkRequest request("http://127.0.0.1:9090" + url);
request.setRawHeader("Content-Type", "application/json");
QNetworkReply* reply = manager.sendCustomRequest(request, request_method, data);
QEventLoop loop;
connec(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
return reply;
}
// ...
void ApiRequest::requestConfig()
{
QNetworkReply* reply = req("/configs", "GET", "");
// ...
reply->deleteLater();
}
You can include QtNetworkReply and QEventLoop classes and use signal/slot mechanism.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QUrl resource(url);
QNetworkRequest request(resource);
QNetworkReply *reply = manager->get(request);
QEventLoop loop;
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
QJsonObject jsonObject = QJsonDocument::fromJson(reply->readAll()).object();

QHttpMultiPart upload file boundary issue

I'm trying to upload some files to a server by using QHttpMultiPart. The following version file is just an example. For some reason, Qt will automatically add boundary into the files. However, what I truly uploaded are .tar.gz files and they will be considered damaged if such boundary were added.
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QNetworkAccessManager *netManager = new QNetworkAccessManager();
HTTPFirmwareUpgrader upgrader(netManager,"http://mytest.com");
upgrader.upgradeCIU();
return app.exec();
}
void HTTPFirmwareUpgrader::upgradeCIU()
{
QString filename = "version";
QString filePath = QString("C:/Users/User/Desktop/HTTP/%1").arg(filename);
qDebug() << uploadFirmware(filename, filePath);
}
bool HTTPFirmwareUpgrader::uploadFirmware(const QString &filename, const QString &filePath)
{
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart myPart;
QFile *file = new QFile(filePath);
if(!file->exists())
qWarning() << "File DOES NOT exists";
file->open(QIODevice::ReadOnly);
myPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("form-data"));
myPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"version\"; filename=\"version\""));
myPart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(ciu);
QByteArray resp = this->post(createUploadRequest(QString("filename=%1").arg(filename)),
multiPart, file);
qDebug() << "Upload Filrmware " << resp;
return resp != "";
}
QByteArray HTTPFirmwareUpgrader::post(QUrl url, QHttpMultiPart *multiPart, QFile *file)
{
QNetworkRequest request;
QEventLoop loop;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("multipart/form-data"));
QNetworkReply *reply = m_manager->post(request, multiPart);
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
multiPart->setParent(reply);
reply->deleteLater();
return reply->readAll();
}
This is the original "version" file:
enter image description here
And this is the "version" file that transferred:
enter image description here
And if I do not set header for the request, the size of the transferred file will be 0.
Does anyone have any idea what should I do?
Problem solved!!!
Turns out I use the wrong way to upload the file. What I need to use is QIODevice instead of QMultipart.

Qt5 - Get Content Length while downloading a file from an Url

I am downloading an ".apk" file from a Url with Get method.
The file successfully donwload on my disk from the server.
I actually want to add a progressbar to my program. THE problem is : I can show the bytesReceived but I can't show the totalBytes of the file I am downloading (ContentLenth). How can I get it please from the server.
Here is what i get on my qDebug while downloading:
3498 of -1
799062 of -1
1923737 of -1
3037550 of -1
3200231 of 3200231
Here is my code:
void DownloadApk::LaunchDownload()
{
QNetworkProxy proxy;
proxy.setType(QNetworkProxy::HttpProxy);
proxy.setHostName("proxy");
proxy.setPort(8080);
QNetworkProxy::setApplicationProxy(proxy);
QUrl url("I put my Url here");
QNetworkRequest request(url);
_file = new QFile("C:/Users/Desktop/testdownload/downloadedFile.apk");
_file->open(QIODevice::WriteOnly);
QNetworkAccessManager *_manager= new QNetworkAccessManager;
_reply = _manager->get(request);// Manager is my QNetworkAccessManager
_file->write(_reply->readAll());
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(error(QNetworkReply::NetworkError)));
connect(_reply, SIGNAL(downloadProgress(qint64, qint64)),
this, SLOT(updateProgress(qint64, qint64)));
connect(_reply, SIGNAL(finished()),
this, SLOT(finished()));
}
void DownloadApk::error(QNetworkReply::NetworkError err)
{qDebug() << err;
// Manage error here.
_reply->deleteLater();
}
void DownloadApk::updateProgress(qint64 read, qint64 total)
{
qDebug() << read <<"of"<<total ;
QByteArray b = _reply->readAll();
QDataStream out(_file);
out << b;
}
void DownloadApk::finished()
{
QMessageBox::information(this, tr("Complete"), tr("Successfully Downloaded"));
// Done
_reply->deleteLater();
_file->close();
// probably delete the file object too
}
I fixed the problem. Actually it was not a QT problem. This Qt code works correctly.
The probleme was from the server that wasn't sending ContentLenth on the header of the reply.

QNetworkAccessManager returning empty results

I am struggling with qnetworkaccessmanager for quite sometime. I googled a lot, but I donot find a solution for this.
I am creating a client using qaccessmanager to talk with a rest server. QNetworkReply is not returning any results. The server is working properly but the client is not returning results. On top of that the server gets called 3 times and sometimes the server is crashing. Hope some one can figure out what is going wrong. I am attaching the client code.
I tried different approches like connecting finished signal of networkaccessmanager, qnetworkreply e.t.c. But all of them ends up in giving the same error "Connection Closed" or the readAll bytearray being empty.
void RestClientCore::ConnectToServer()
{
m_NetworkManager = new QNetworkAccessManager(this);
QUrl url("http://localhost");
url.setPort(5432);
QByteArray postData;
postData.append("/?userid=user");
postData.append("&site=site");
QNetworkReply *reply = m_NetworkManager->post(request,postData);
connect(reply, SIGNAL(readyRead()),this, SLOT(slotReadyRead()));
connect(reply, SIGNAL(finished()), this, SLOT(onRequestCompleted()));
}
void RestClientCore::onRequestCompleted() {
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if(reply->error())
{
qDebug() <<reply->bytesAvailable() << reply->errorString();
}
else
{
qDebug() << reply->readAll();
}
reply->deleteLater();
}
void RestClientCore::slotReadyRead()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
qDebug() << reply->readAll();
}
Thanks in advance
Regards
Rejo

Resources