Error during multipart upload using Qt - c++

I am trying to upload a file using QNetworkAccessManager, but I always get an error (Error transferring url - server replied: Bad Request). Below is my code
QString name = "Simple.txt";
QString type = "text/plain; charset=utf-8";
QString uploadUrl = "myuploadUrl";
// setup the multipart request
QString bound="---------------------------723690991551375881941828858";
QByteArray data(QString("--"+bound+"\r\n").toLatin1());
// write the file using standard method for multipart file upload
data += "Content-Disposition: form-data; name=\"file\"; filename=\""+name.toLatin1()+"\"\r\n";
data += "Content-Type: "+type.toLatin1()+"\r\n\r\n";
data += "Hello, I am simple file";
data += "\r\n";
data += "--" + bound;
qDebug() << data;
// make the request with appropriate headers
QNetworkRequest request(QUrl(uploadUrl));
request.setRawHeader(QByteArray("Content-Type"),QString("multipart/form-data; boundary=" + bound).toLatin1());
request.setRawHeader(QByteArray("Content-Length"), QString::number(data.length()).toLatin1());
QNetworkReply *reply = networkManager->post(request,data);
QObject::connect(reply, &QNetworkReply::finished, this, FileUploader::requestFinished);
However running this python script, which should do the same thing, works.
import requests
import json
file_name = "Simple.txt"
uploadUrl = "myUploadUrl";
resp = requests.post(uploadUrl, data=r["data"], files={"file": open(file_name, "rb")})
print (resp);

The problem was fixed by adding a data += "\r\n" at the end of the data.

Instead of creating request manually use QT internals. Here You can try this code which is working for me.
void OfflineLogHandler::uploadOfflineLogFile(QString filePath){
QUrl targateUlr(APIUrls::getInstance()->getFlightLogUploadURL()); //pass URL here
APIUrls *apiUrls = APIUrls::getInstance();
qDebug()<<"Target upload offline log url:"<<targateUlr;
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart formHeader;
formHeader.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"\"; filename=\""+filePath.section("/",-1)+"\""));
if(filePath.contains(".json"))
formHeader.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
else if(filePath.contains(".csv"))
formHeader.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/csv"));
QFile *file = new QFile(filePath);
file->open(QIODevice::ReadOnly);
formHeader.setBodyDevice(file);
multiPart->append(formHeader);
file->setParent(multiPart);
QNetworkRequest request;
request.setUrl(targateUlr);
QString CredData = apiUrls->getCredentials();
QString headerData = apiUrls->getApiAuthorizationType() + CredData;
qDebug()<<"access:"<<headerData.toLocal8Bit();
request.setRawHeader( "Authorization", headerData.toLocal8Bit() );
request.setRawHeader("CustomeData1","Data1");
request.setRawHeader("CustomeData2","Data2");
request.setHeader(QNetworkRequest::ContentTypeHeader,QString("multipart/form-data; boundary="+multiPart->boundary()).toLatin1());
QNetworkAccessManager *restclient; //in class
restclient = new QNetworkAccessManager(); //constructor
disconnect(restclient,SIGNAL(finished(QNetworkReply*)), this, nullptr);
connect(restclient, SIGNAL(finished(QNetworkReply*)), this, SLOT(handleFileUploadResponse(QNetworkReply*)));
networkReply = restclient->post(request,multiPart);
connect(networkReply,SIGNAL(uploadProgress(qint64, qint64)),this,SLOT(fileUprogress(qint64, qint64)));
qDebug()<<"net:"<<networkReply;
}

Related

C++ QHttpMultiPart

I have to send the image through the REST API service from my test C++ QT client to get some information about it.
I've been trying to figure out for very long time, what's wrong with my POST request according follow documentation...
link: https://docs.facecloud.tevian.ru/
So i do it next way using QHttpMultiPart and QNetworkReply
QUrl testUrl("https://backend.facecloud.tevian.ru/api/v1/photos");
QNetworkRequest request(testUrl);
//auth token i've got before
request.setRawHeader(QByteArray("Authorization"), token.toUtf8());
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart firstPart;
const QString header = "application/json";
firstPart.setHeader(QNetworkRequest::ContentTypeHeader, header);
QJsonObject req;
QJsonDocument doc;
QByteArray data;
req.insert("fd_min_size",0);
req.insert("fd_max_size",0);
req.insert("fd_threshold",0.8);
req.insert("rotate_until_faces_found",true);
req.insert("orientation_classifier",true);
QJsonArray array;
array.push_back("0");
array.push_back("0");
array.push_back("1000");
array.push_back("1000");
req.insert("face", array);
req.insert("person_id",1);
doc = QJsonDocument(req);
data = doc.toJson();
firstPart.setBody(data);
QString img_path = "C:/Users/brode/Downloads/hi.jpg";
QString img_name = "hi.jpg";
QHttpPart secondPart;
secondPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
secondPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"preview_file\"; filename=\""+ img_name+ "\""));
QFile *file = new QFile(img_path);
if (!file->exists()) {
return;
}
file->open(QIODevice::ReadOnly);
secondPart.setBodyDevice(file);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(firstPart);
multiPart->append(secondPart);
QNetworkReply* reply = manager.post(request, multiPart);
multiPart->setParent(reply); // delete the multiPart with the reply
connect(reply, &QNetworkReply::readyRead,this, &rest_handler::readyRead);
First time i faced REST API...What i've done wrong?
After playing around with the api you are using, You are using wrong request format, the endpoint expects request with Content-Type: image/jpeg not using multipart. Also values like fd_min_size, face... should be sent as query parameters not in request body. The following piece of code works fine:
double fd_min_size = 0;
double fd_max_size = 0;
double fd_threshold=0.8;
bool rotate_until_faces_found = false;
bool orientation_classifier = false;
QVector <int>face = {0, 0, 1000, 1000};
int person_id = 1;
QString path = "https://backend.facecloud.tevian.ru/api/v1/photos";
// You don't have to add them all.
path += "?fd_min_size=" + QString::number(fd_min_size);
path += "&fd_max_size=" + QString::number(fd_max_size);
path += "&fd_threshold=" + QString::number(fd_threshold);
path += "&rotate_until_faces_found=" + (rotate_until_faces_found ? QString("true") : QString("false"));
path += "&orientation_classifier=" + (orientation_classifier ? QString("true") : QString("false"));
path += "&face=";
for(int i = 0;i < static_cast<int>(face.size());i++) {
path += QString::number(face[i]);
if(i + 1 != static_cast<int>(face.size())) {
path += ",";
}
}
path += "&person_id=" + QString::number(person_id); // The only one required
QUrl testUrl(path);
QNetworkRequest request(testUrl);
//auth token i've got before
request.setRawHeader(QByteArray("Authorization"), "Bearer " + token.toUtf8());
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
QString img_path = "C:/Users/xxx/Pictures/face.jpg";
QFile *file = new QFile(img_path);
if (!file->exists() || !file->open(QIODevice::ReadOnly)) {
return;
}
auto manager = new QNetworkAccessManager();
QNetworkReply *reply = manager->post(request, file);

issue with reading a data from web page

I want to read a string from html source of https web page.
Here is my code:
QString qmldialogue::readPage(QString page)
{
QString url = "https://my link here";
QNetworkAccessManager manager;
QNetworkReply *response = manager.get(QNetworkRequest(QUrl(url)));
QEventLoop event;
connect(response, SIGNAL(finished()), &event, SLOT(quit()));
event.exec();
QByteArray bytes = response->readAll();
QString str = QString::fromUtf8(bytes.data(), bytes.size());
int statusCode = response->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug()<<str;
qDebug() << QVariant(statusCode).toString();
return str;
}
Everything seems to be working, but for some reason the content of the html tags I need is empty:
...class=\"status-value cert-name\"></span></div>...
If I manually download the html file of the page, then everything will be as I need:
...class="status-value cert-name">Valid until termlessly</span></div>...
Int statusCode value is 200.
What am I doing wrong?

Qt Json Parsing c++

I am trying to parse json object in QT from an api. However when i try the codes written below i cannot parse the object. I simply want to get the information stored in those parameters.
API is
{
"error": {
"errorcode": 0,
"errorstring": ""
},
"login": 1,
"logintoken": "209b06aa7f059673db393ff7a731af1847344903b9733f026cc3a6f7a5b797b3"
}
The Codes are
QUrl ava_url("http://pinundpin.de/wsdemo/atgdemoapi_v3/index.php");
QNetworkRequest ava_request(ava_url);
ava_request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
QNetworkAccessManager *manager = new QNetworkAccessManager();
QEventLoop loop;
QObject::connect(manager,
SIGNAL(finished(QNetworkReply *)),
&loop,
SLOT(quit()));
QByteArray postData;
Username = "testwebser";
Passwd = "2017#QWEasdZXC";
postData.append("action=login&");
postData.append("password="+Passwd+"&");
postData.append("username="+Username);
QNetworkReply* reply = manager->post(ava_request,postData);
loop.exec();
QString response = (QString)reply->readAll();
qDebug()<< response;
QJsonDocument temp = QJsonDocument::fromJson(response.toUtf8());
QJsonObject jsonObj = temp.object();
qDebug() <<"error"<< jsonObj["error"].toString();
qDebug() <<"login"<< jsonObj["login"].toString();
qDebug() << "logintoken"<<jsonObj["logintoken"].toString();
the response string looks
and the output is
D/libAndroisShop.so(21944): ../AndroisShop/networkconnection.cpp:45 (QString NetworkConnection::Connect(QString, QString)): "<br><br>NO OF ROWDATA: 1{\"error\":{\"errorcode\":0,\"errorstring\":\"\"},\"login\":1,\"logintoken\":\"4daaf6b3dd5a26a2ad2c436e564bfa4d6c439ce4d0d6cd66705a8bdadddddaa0\"}"
D/libAndroisShop.so(21944): ../AndroisShop/networkconnection.cpp:50 (QString NetworkConnection::Connect(QString, QString)): error ""
D/libAndroisShop.so(21944): ../AndroisShop/networkconnection.cpp:51 (QString NetworkConnection::Connect(QString, QString)): login ""
D/libAndroisShop.so(21944): ../AndroisShop/networkconnection.cpp:52 (QString NetworkConnection::Connect(QString, QString)): logintoken ""
Postman Image
If you review the result of POSTMAN in raw mode you get the following:
What QNAM gets but POSTMAN has another algorithm to extract the json:
So a possible solution is to remove the unwanted element: <br><br>NO OF ROWDATA: 1, for this it takes advantage that the json must start with "{" so the superior substring is used.
QUrl ava_url("http://pinundpin.de/wsdemo/atgdemoapi_v3/index.php");
QNetworkRequest ava_request(ava_url);
ava_request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
QNetworkAccessManager *manager = new QNetworkAccessManager();
QEventLoop loop;
QObject::connect(manager,
&QNetworkAccessManager::finished,
&loop,
&QEventLoop::quit);
QByteArray postData;
QString Username = "testwebser";
QString Passwd = "2017#QWEasdZXC";
postData.append("action=login&");
postData.append("password="+Passwd+"&");
postData.append("username="+Username);
QNetworkReply *reply = manager->post(ava_request,postData);
loop.exec();
QByteArray response = reply->readAll();
QByteArray json = response.mid(response.indexOf("{"));
QJsonDocument temp = QJsonDocument::fromJson(json);
QJsonObject jsonObj = temp.object();
QJsonObject errorObj = jsonObj["error"].toObject();
qDebug()<<"error: "
<<"errorcode: "<< errorObj["errorcode"].toInt()
<<"errorstring: "<< errorObj["errorstring"].toString();
qDebug() <<"login"<< jsonObj["login"].toInt();
qDebug() << "logintoken"<<jsonObj["logintoken"].toString();
Output:
error: errorcode: 0 errorstring: ""
login 1
logintoken "cc7e46f34e0a268cecbaaeaad41b0ae2727cc7196b632574a4db16544576d012"

Adding attachment to JIRA issue using the REST API and Qt with QNetworkRequest

I'm trying to add an attacment to an existing JIRA issue using the REST API and Qt.
When I run the code below, the reply is an empty array ("[]").
edit: updated the code
QString APIhandler::attachFile(QString fileloc, QString issueKey, QString cookie)
{
//create multiPart
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QFile *file = new QFile(fileloc);
//create httpPart for file
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\""+ file->fileName()+ "\""));
file->open(QIODevice::ReadOnly);
filePart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(filePart);
QNetworkAccessManager *mngr = new QNetworkAccessManager();
QUrl issurl(baseURL + "/api/2/issue/"+ issueKey + "/attachments");
QNetworkRequest req(issurl);
QNetworkReply *reply ;
QEventLoop loop;
//add headers
req.setRawHeader("cookie", "JSESSIONID = " + cookie.toUtf8()); // the session cookie
req.setRawHeader("X-Atlassian-Token", "nocheck");
req.setRawHeader("Content-Type", "multipart/form-data; boundary=------------------------53a5a2cd1d9c8b7f");
//req.setRawHeader("Content-Length", postDataSize);
reply = mngr->post(req, multiPart);
multiPart->setParent(reply);
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
return reply->readAll();
}
I am using the JIRA REST API documentation and qt documentation for reference, and looking off of this java implementation (which I've tried to replicate).
It seems like I'm either missing a header, or adding the file incorrectly.
Any help is greatly appreciated!
EDIT - Here's part of a wireshark comparing the example from the api using curl (LEFT) , and my code (RIGHT). The one on the left works, and clearly has different MIME data, but I'm not sure how to implement that in Qt
Okay, so I figured it out. I might be the only one on earth who is using (or will use) Qt to interact with the JIRA API, but for posterity, here's what I came up with:
QString APIhandler::attachFile(QString fileloc, QString issueKey, QString cookie)
{
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart filePart;
QFileInfo fileInfo(fileloc);
//what I wasn't doing before!
multiPart->setBoundary("------------------------53a5a2cf4d9c8b7f");
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\""+fileInfo.fileName() +"\""));
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
QFile *file = new QFile(fileloc);
file->open(QIODevice::ReadOnly);
filePart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(filePart);
QNetworkAccessManager *mngr = new QNetworkAccessManager();
QUrl issurl(baseURL + "/api/2/issue/"+ issueKey + "/attachments");
QNetworkRequest req(issurl);
QNetworkReply *reply ;
QEventLoop loop;
//add headers
req.setRawHeader("X-Atlassian-Token", "nocheck");
req.setRawHeader("cookie", "JSESSIONID = " + cookie.toUtf8()); // the session cookie
req.setRawHeader("Content-Type", "multipart/form-data;boundary=------------------------53a5a2cf4d9c8b7f");
reply = mngr->post(req, multiPart);
multiPart->setParent(reply);
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
//read the reply
QByteArray bytes=reply->readAll();
//return the reply JSON
return QString::fromUtf8(bytes.data(), bytes.size());
delete file;
delete multiPart;
delete reply;
delete mngr;
}
The key part here, and what I was doing wrong, was the way in which I set the boundary for the multipart. Instead of setting it in the header, I should have used:
multipart->setBoundary()
Which you can see reflected above.
If you're coming across this and are going to use it, I'd recommend cleaning it up a bit, first. But it works!

on uploading file to google-drive by using c++/qt filename of newfile is always untitled

I found a similar question: Google Drive API insert new file with Untitled name
but code is not attached there.
My code:
void googled::newuploadSettings(QNetworkReply *reply){
QByteArray data = reply->readAll();
qDebug() << data;
QString x = getValue(data,"access_token");
qDebug() << x;
x = "Bearer " + x;
qDebug() << x;
QNetworkRequest request;
//QString contentType = "text/plain";
QUrl url("https://www.googleapis.com/upload/drive/v2/files?uploadType=media");
//url.addQueryItem("originalFilename","sp");
request.setUrl(url);
request.setRawHeader("Content-Length","200000000");
request.setRawHeader("Content-Type","image/jpeg");
request.setRawHeader("title","sp");
//request.setRawHeader("X-Upload-Content-Length","20000000");
//request.setRawHeader("X-Upload-Content-Type","image/jpeg");
request.setRawHeader("Authorization",x.toLatin1());
//request.setRawHeader("Host","https://www.googleapis.com");
qDebug() << getValue(data,"access_token").toUtf8();
//request.setRawHeader()
QFile file("/home/saurabh/Pictures/005.jpg");
//file.setFileName("kashmir");
file.open(QIODevice::ReadOnly);
QByteArray arr = file.readAll();
file.close();
qDebug() << "file";
QString str = "a";
str.append(arr);
qDebug() << str;
m_netM = new QNetworkAccessManager;
QObject::connect(m_netM, SIGNAL(finished(QNetworkReply *)),
this, SLOT(uploadfinishedSlot(QNetworkReply *)));
m_netM->post(request,arr);
}
I know I have to set title field for giving name to file but am not able to figure out how I would do it in qt
title option should go to request body according to Files.insert() documentation
For instance, if header has
Content-Type: application/json
request body would be
{
"title": "sp"
}
To set title of the file to be "sp". Please look for qt documentation to find how to set request body.