Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [SOLVED] QNetworkAccessManager - Adding a multipart/form-data to a POST request

[SOLVED] QNetworkAccessManager - Adding a multipart/form-data to a POST request

Scheduled Pinned Locked Moved General and Desktop
10 Posts 2 Posters 15.3k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    maximus
    wrote on 24 Jul 2015, 18:26 last edited by maximus
    #1

    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-file

    It 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!


    Free Indoor Cycling Software - https://maximumtrainer.com

    1 Reply Last reply
    0
    • M Offline
      M Offline
      mchinand
      wrote on 24 Jul 2015, 18:51 last edited by
      #2

      I think for the 'file', you have to give it the data in the file not just the file name. In their example that uses the curl command, that's the purpose of the @ before the filename (see the curl manpage).

      M 1 Reply Last reply 24 Jul 2015, 19:14
      0
      • M mchinand
        24 Jul 2015, 18:51

        I think for the 'file', you have to give it the data in the file not just the file name. In their example that uses the curl command, that's the purpose of the @ before the filename (see the curl manpage).

        M Offline
        M Offline
        maximus
        wrote on 24 Jul 2015, 19:14 last edited by maximus
        #3

        @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 QByteArray

        I think I need to use QHttpMultiPart, still not familiar with it tought.


        Free Indoor Cycling Software - https://maximumtrainer.com

        1 Reply Last reply
        0
        • M Offline
          M Offline
          mchinand
          wrote on 24 Jul 2015, 19:27 last edited by
          #4

          I'm not sure either what you have to do with the data before using it with addQueryItem(). The curl manpage seems to indicate that it only strips out carriage returns and newlines when used like in Strava's example.

          M 1 Reply Last reply 24 Jul 2015, 19:32
          0
          • M mchinand
            24 Jul 2015, 19:27

            I'm not sure either what you have to do with the data before using it with addQueryItem(). The curl manpage seems to indicate that it only strips out carriage returns and newlines when used like in Strava's example.

            M Offline
            M Offline
            maximus
            wrote on 24 Jul 2015, 19:32 last edited by maximus
            #5

            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;
            

            }


            Free Indoor Cycling Software - https://maximumtrainer.com

            1 Reply Last reply
            0
            • M Offline
              M Offline
              mchinand
              wrote on 24 Jul 2015, 19:46 last edited by
              #6

              Looks like your authkey should be in the header like in the curl example:

              "Authorization: Bearer 83ebeabdec09f6670863766f792ead24d61fe3f9"
              

              Of course, you use your own authkey.

              M 1 Reply Last reply 24 Jul 2015, 19:58
              0
              • M mchinand
                24 Jul 2015, 19:46

                Looks like your authkey should be in the header like in the curl example:

                "Authorization: Bearer 83ebeabdec09f6670863766f792ead24d61fe3f9"
                

                Of course, you use your own authkey.

                M Offline
                M Offline
                maximus
                wrote on 24 Jul 2015, 19:58 last edited by
                #7

                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


                Free Indoor Cycling Software - https://maximumtrainer.com

                1 Reply Last reply
                0
                • M Offline
                  M Offline
                  mchinand
                  wrote on 24 Jul 2015, 20:49 last edited by
                  #8

                  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--
                  
                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    maximus
                    wrote on 24 Jul 2015, 22:31 last edited by maximus
                    #9

                    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.


                    Free Indoor Cycling Software - https://maximumtrainer.com

                    1 Reply Last reply
                    0
                    • M Offline
                      M Offline
                      maximus
                      wrote on 25 Jul 2015, 20:07 last edited by
                      #10

                      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
                      

                      Free Indoor Cycling Software - https://maximumtrainer.com

                      1 Reply Last reply
                      0

                      9/10

                      24 Jul 2015, 22:31

                      • Login

                      • Login or register to search.
                      9 out of 10
                      • First post
                        9/10
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved