[SOLVED] QNetworkAccessManager - Adding a multipart/form-data to a POST request
-
I am trying to communicate with a web service to upload a file.
Here is the web service : http://strava.github.io/api/v3/uploads/#post-fileIt seems I am unable to attach the file in the POST request. I have been looking for a way to do that with QNetworkAccessManager so I can do the POST request directly in Qt.
Here is my current code, The server is answering with this error :
"Error downloading https://www.strava.com/api/v3/uploads - server replied: Bad Request"QNetworkReply* ExtRequest::stravaUploadFile(QString access_token, QString activityName, QString activityDescription, QString pathToFile) { QNetworkAccessManager *managerWS = qApp->property("NetworkManagerWS").value<QNetworkAccessManager*>(); const QString url = "https://www.strava.com/api/v3/uploads"; QUrlQuery postData; postData.addQueryItem("access_token", access_token); // postData.addQueryItem("activity_type", "workout"); // postData.addQueryItem("name", activityName); // postData.addQueryItem("description", activityDescription); // postData.addQueryItem("trainer", QString::number(1)); //Temporary test file QFile file("C:/test3.fit"); QByteArray blob; if (file.open(QIODevice::ReadOnly)) { blob = file.readAll(); } qDebug() << "blob size is" << blob.size(); postData.addQueryItem("file", blob); postData.addQueryItem("data_type", "fit"); QNetworkRequest request; request.setUrl(url); request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded"); QNetworkReply *replyPutUser = managerWS->post(request, postData.toString(QUrl::FullyEncoded).toUtf8() ); return replyPutUser; }
Thanks for your help!
-
@mchinand Thanks for the heads up, really newbie with curl!
I'm now trying to put the data directly instead of the file name. (updated code up here)
Not sure what kind of data use curl, doesn't seem to work with the file to QByteArrayI think I need to use QHttpMultiPart, still not familiar with it tought.
-
I've been trying this alternative, inspired from Qt doc.
Not luck so far, don't see how to attach the "access_token" in the textPart
QNetworkReply* ExtRequest::stravaUploadFile2(QString access_token, QString activityName, QString activityDescription, QString pathToFile) { QNetworkAccessManager *managerWS = qApp->property("NetworkManagerWS").value<QNetworkAccessManager*>(); QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart textPart; textPart.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded"); textPart.setBody("my text"); // textPart.setBody(QByteArray); //How to set parameters like with QUrlQuery postData.addQueryItem("access_token", access_token); QHttpPart imagePart; imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("file/fit")); imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data;")); QFile *file = new QFile("C:/test3.fit"); 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("https://www.strava.com/api/v3/uploads"); QNetworkRequest request(url); QNetworkReply *reply = managerWS->post(request, multiPart); multiPart->setParent(reply); // delete the multiPart with the reply return reply;
}
-
Yes the authkey can be passed in the header or in the parameters.
I still need to pass other parameters, like "data_type", so I'm passing both in parameters.
Just haven't figured how to create multiple parameters with QHttpPart.
Wish I could use curl directly!Thanks
-
Looks like each form parameter gets its own part. Here's what the curl command is sending (by adding the --trace-ascii tracefile.txt option)
=> Send header, 287 bytes (0x11f) 0000: POST /api/v3/uploads HTTP/1.1 001f: Host: www.strava.com 0035: User-Agent: curl/7.42.1 004e: Accept: */ 005b: Authorization: Bearer 83ebeabdec09f6670863766f792ead24d61fe3f9 009b: Content-Length: 1182 00b1: Expect: 100-continue 00c7: Content-Type: multipart/form-data; boundary=-------------------- 0107: ----5812b831d34f5844 011d: <= Recv header, 23 bytes (0x17) 0000: HTTP/1.1 100 Continue => Send data, 262 bytes (0x106) 0000: --------------------------5812b831d34f5844 002c: Content-Disposition: form-data; name="activity_type" 0062: 0064: ride 006a: --------------------------5812b831d34f5844 0096: Content-Disposition: form-data; name="file"; filename="Activity. 00d6: fit" 00dc: Content-Type: application/octet-stream 0104: => Send data, 771 bytes (0x303) 0000: ..d......FIT@.....................^?...)........@...1.......@...1 0040: .......A....................A.....................).......B..... 0080: ...................).....a..........3...).....a..........3...).. 00c0: ...a..........3...).....a9.........3...).....a@...y.....3...)... 0100: ..aF...r...#.3...).....aJ...l...).3...).....aw.......r.3...).... 0140: .a..........3.\.).....a....<.....3...).....a........_.3...)..... 0180: a....y.....3...).....a_.........3.3.).....a....W...=.3.p.)...... 01c0: .C.............................................................. 0200: ....)...).....a.......a....W..5...5....=.........p........A..... 0240: ................)..........D.................................... 0280: .................................)...).....a.......5...5....=... 02c0: ......p.............E...".......................).....5.)..c.... 0300: ... => Send data, 149 bytes (0x95) 0000: 0002: --------------------------5812b831d34f5844 002e: Content-Disposition: form-data; name="data_type" 0060: 0062: fit 0067: --------------------------5812b831d34f5844--
-
Interesting!
Any way that you know to emulate that same curl command with QNetworkAccessManager?
I guess I would have to send an header with the Content-Type "application/octet-stream" in a "QHttpMultiPart" , now to see if I can convert the file to this format somehow? My QByteArray get refused by the API.
Will try some other stuff, in last resort, I will just redirect my request to my webserver (my app is requires Internet connection) instead of using Qt and create a normal curl request there. -
Yes finally got it to work! Thanks for your message with the curl log, that helped me so much.
I just had to add the "filename" in the fileDataPart and it worked.QHttpPart fileDataPart; fileDataPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"test3.fit\"")); fileDataPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream")); QFile *file = new QFile("C:/test3.fit"); file->open(QIODevice::ReadOnly); qDebug() << "test file size:" << file->size(); fileDataPart.setBodyDevice(file); file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart