Михаиллл
14 мая 2020 г. 18:41

Как через POST запрос отправить файл

Добрый день.
Пытаюсь в json запихнуть картинку и отправить, но получаю ошибку

  1. 413 Entity too large

Скажите пожалуйста, как все же отправить картинку POST запросом?

  1. void AppCore::slotApiRequest(QByteArray data)
  2. {
  3. QNetworkRequest request;
  4. request.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );
  5. request.setUrl(QUrl(urlServerForRequest));
  6. request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); //разрешает перенаправление
  7. request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
  8. request.setHeader(QNetworkRequest::ContentLengthHeader,QByteArray::number(data.size()));
  9. myApiQuery.post(request, data);
  10. }
  11.  
  12. void AppCore::addCategory(QString nameCategory, QString fileName)
  13. {
  14. fileName = fileName.remove("file:///");
  15. //qDebug()<<nameCategory<<fileName;
  16. QImage myImage(fileName);
  17.  
  18. QByteArray bArray;
  19. QBuffer buffer(&bArray);
  20. buffer.open(QIODevice::WriteOnly);
  21. myImage.save(&buffer, "BMP");
  22. QString imageString("data:image/bmp;base64,");
  23. imageString.append(QString::fromLatin1(bArray.toBase64().data()));
  24. //qDebug()<<imageString;
  25.  
  26. QJsonObject jObj = QJsonObject{{"nameCategory", nameCategory}, {"image", imageString}};
  27. //QJsonObject jObj = QJsonObject{{"nameCategory", nameCategory}, {"image", fileName}};
  28. QJsonDocument doc = QJsonDocument(jObj);
  29. QByteArray arrayJson;
  30. QString strJson(doc.toJson(QJsonDocument::Compact));
  31. arrayJson += strJson;
  32.  
  33. urlServerForRequest= urlServer + "addCategory";
  34. slotApiRequest(arrayJson);
  35. }
2

Вам это нравится? Поделитесь в социальных сетях!

16
Evgenii Legotckoi
  • 14 мая 2020 г. 18:46

Добрый день.

Это вам не C++ код копать нужно, а сервер настраивать. Если на сервере nginx, то настройте максимальный размер запроса.
Он вас просто отпинывает из-за слишком большого тела запроса.

Вот для максимального размера в 2 мб

  1. client_max_body_size 2m;
    Evgenii Legotckoi
    • 14 мая 2020 г. 18:52

    Ну или уменьшайте размер изображения до того, который пропустит сервер.

      Михаиллл
      • 14 мая 2020 г. 19:05

      Я использую QtWebApp. Вы случайно не знаете, как в нем настраивать?

        Evgenii Legotckoi
        • 14 мая 2020 г. 19:10

        Глянул документацию, там нужно колупать структуру HttpConnectionHandlerSettings

        В ней есть поля maxRequestSize и maxMultipartSize

        • maxRequestSize - размер тела запроса
        • maxMultipartSize - размера запроса для изображений и прочей медия ерунды

        Поищите, как установить maxMultipartSize больше, чем 1048576

        В каком конкретно месте это делать, я не знаю. Но думаю, что-то такое при инициализации нужно настраивать

          Михаиллл
          • 14 мая 2020 г. 19:32
          • (ред.)

          Нашел эти настройки в файле .ini
          Сделал так, но почему то все равно запрос не доходит, и узнал что размер моего запроса 51321 символов.
          Скажите пожалуйста, в чем еще могут быть трудности?

          1. [listener]
          2. ; host=192.168.0.100
          3. port=8080
          4. minThreads=4
          5. maxThreads=100
          6. cleanupInterval=60000
          7. readTimeout=60000
          8. maxRequestSize=10000000
          9. maxMultiPartSize=10000000
          10.  

          И ошибок не выдает, ту ошибку выдало только один раз, в остальных предыдущих случаях все проходило так же без ошибок.

            Evgenii Legotckoi
            • 14 мая 2020 г. 20:05

            Не понял фразу

            И ошибок не выдает, ту ошибку выдало только один раз, в остальных предыдущих случаях все проходило так же без ошибок.

            То есть ошибок нет, но загрузить не получается?

            узнал что размер моего запроса 51321 символов.

            Символов или байт? в зависимости от кодировки символ может занимать большее количество байт, чем один.

            Также ещё поменяйте заголовок запроса так (что-то я не обратил внимания сразу, вы ведь изображение грузите)

            1. request.setHeader( QNetworkRequest::ContentTypeHeader, "multipart/form-data" );
              Михаиллл
              • 14 мая 2020 г. 20:43

              Верно, ошибок нет, но загрузить не получается.
              Размер я узнаю методос QByteArray::size и вероятней всего получаю количество символов.
              Попробовал поменять заголовок, но результат тот же.
              Может быть как то иначе файлы нужно подгружать?

                Evgenii Legotckoi
                • 15 мая 2020 г. 13:03

                Вы у себя в коде дважды используете

                1. request.setHeader( QNetworkRequest::ContentTypeHeader, "" );

                Попробуйте убрать тот, что с application/json

                  Михаиллл
                  • 15 мая 2020 г. 13:11

                  Сделал так

                  1. QNetworkRequest request;
                  2. //request.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );
                  3. request.setUrl(QUrl(urlServerForRequest));
                  4. request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); //разрешает перенаправление
                  5. request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
                  6. //request.setHeader( QNetworkRequest::ContentTypeHeader, "multipart/form-data" );
                  7. request.setHeader(QNetworkRequest::ContentLengthHeader,QByteArray::number(data.size()));
                  8. qDebug()<<data.size();
                  9. myApiQuery.post(request, data);

                  и получил в первый раз ошибку

                  1. "413 Entity too large\r\n"

                  а больше ошибок не выдавало, при этом запрос не проходит.
                  так тоже не работает, но ошибок не выдает

                  1. QNetworkRequest request;
                  2. //request.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );
                  3. request.setUrl(QUrl(urlServerForRequest));
                  4. request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); //разрешает перенаправление
                  5. //request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
                  6. request.setHeader( QNetworkRequest::ContentTypeHeader, "multipart/form-data" );
                  7. request.setHeader(QNetworkRequest::ContentLengthHeader,QByteArray::number(data.size()));
                  8. qDebug()<<data.size();
                  9. myApiQuery.post(request, data);
                    Evgenii Legotckoi
                    • 15 мая 2020 г. 13:17

                    всё... вот теперь у меня идеи закончились (((

                    Могу только предложить покопаться в исходника QtWebApp, посмотреть в каком случае появляется та ошибка и попытаться её исправить.

                    Ещё возможно, что саму пересылку изображения нужно как-то иначе паковать. То есть не через "data:image/bmp;base64,"

                      Михаиллл
                      • 15 мая 2020 г. 13:57
                      • (ред.)

                      Тут удобно смотреть и искать.
                      Ошибку выдает эта функция. Почему-то срабатывает условие (30 стр) "currentRequest->getStatus()==HttpRequest::abort"

                      1. void HttpConnectionHandler::read()
                      2. {
                      3. // The loop adds support for HTTP pipelinig
                      4. while (socket->bytesAvailable())
                      5. {
                      6. #ifdef SUPERVERBOSE
                      7. qDebug("HttpConnectionHandler (%p): read input",static_cast<void*>(this));
                      8. #endif
                      9.  
                      10. // Create new HttpRequest object if necessary
                      11. if (!currentRequest)
                      12. {
                      13. currentRequest=new HttpRequest(settings);
                      14. }
                      15.  
                      16. // Collect data for the request object
                      17. while (socket->bytesAvailable() && currentRequest->getStatus()!=HttpRequest::complete && currentRequest->getStatus()!=HttpRequest::abort)
                      18. {
                      19. currentRequest->readFromSocket(socket);
                      20. if (currentRequest->getStatus()==HttpRequest::waitForBody)
                      21. {
                      22. // Restart timer for read timeout, otherwise it would
                      23. // expire during large file uploads.
                      24. int readTimeout=settings->value("readTimeout",10000).toInt();
                      25. readTimer.start(readTimeout);
                      26. }
                      27. }
                      28.  
                      29. // If the request is aborted, return error message and close the connection
                      30. if (currentRequest->getStatus()==HttpRequest::abort)
                      31. {
                      32. socket->write("HTTP/1.1 413 entity too large\r\nConnection: close\r\n\r\n413 Entity too large\r\n");
                      33. while(socket->bytesToWrite()) socket->waitForBytesWritten();
                      34. socket->disconnectFromHost();
                      35. delete currentRequest;
                      36. currentRequest=nullptr;
                      37. return;
                      38. }
                      39.  
                      40. // If the request is complete, let the request mapper dispatch it
                      41. if (currentRequest->getStatus()==HttpRequest::complete)
                      42. {
                      43. readTimer.stop();
                      44. qDebug("HttpConnectionHandler (%p): received request",static_cast<void*>(this));
                      45.  
                      46. // Copy the Connection:close header to the response
                      47. HttpResponse response(socket);
                      48. bool closeConnection=QString::compare(currentRequest->getHeader("Connection"),"close",Qt::CaseInsensitive)==0;
                      49. if (closeConnection)
                      50. {
                      51. response.setHeader("Connection","close");
                      52. }
                      53.  
                      54. // In case of HTTP 1.0 protocol add the Connection:close header.
                      55. // This ensures that the HttpResponse does not activate chunked mode, which is not spported by HTTP 1.0.
                      56. else
                      57. {
                      58. bool http1_0=QString::compare(currentRequest->getVersion(),"HTTP/1.0",Qt::CaseInsensitive)==0;
                      59. if (http1_0)
                      60. {
                      61. closeConnection=true;
                      62. response.setHeader("Connection","close");
                      63. }
                      64. }
                      65.  
                      66. // Call the request mapper
                      67. try
                      68. {
                      69. requestHandler->service(*currentRequest, response);
                      70. }
                      71. catch (...)
                      72. {
                      73. qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler",
                      74. static_cast<void*>(this));
                      75. }
                      76.  
                      77. // Finalize sending the response if not already done
                      78. if (!response.hasSentLastPart())
                      79. {
                      80. response.write(QByteArray(),true);
                      81. }
                      82.  
                      83. qDebug("HttpConnectionHandler (%p): finished request",static_cast<void*>(this));
                      84.  
                      85. // Find out whether the connection must be closed
                      86. if (!closeConnection)
                      87. {
                      88. // Maybe the request handler or mapper added a Connection:close header in the meantime
                      89. bool closeResponse=QString::compare(response.getHeaders().value("Connection"),"close",Qt::CaseInsensitive)==0;
                      90. if (closeResponse==true)
                      91. {
                      92. closeConnection=true;
                      93. }
                      94. else
                      95. {
                      96. // If we have no Content-Length header and did not use chunked mode, then we have to close the
                      97. // connection to tell the HTTP client that the end of the response has been reached.
                      98. bool hasContentLength=response.getHeaders().contains("Content-Length");
                      99. if (!hasContentLength)
                      100. {
                      101. bool hasChunkedMode=QString::compare(response.getHeaders().value("Transfer-Encoding"),"chunked",Qt::CaseInsensitive)==0;
                      102. if (!hasChunkedMode)
                      103. {
                      104. closeConnection=true;
                      105. }
                      106. }
                      107. }
                      108. }
                      109.  
                      110. // Close the connection or prepare for the next request on the same connection.
                      111. if (closeConnection)
                      112. {
                      113. while(socket->bytesToWrite()) socket->waitForBytesWritten();
                      114. socket->disconnectFromHost();
                      115. }
                      116. else
                      117. {
                      118. // Start timer for next request
                      119. int readTimeout=settings->value("readTimeout",10000).toInt();
                      120. readTimer.start(readTimeout);
                      121. }
                      122. delete currentRequest;
                      123. currentRequest=nullptr;
                      124. }
                      125. }
                      126. }
                        Evgenii Legotckoi
                        • 15 мая 2020 г. 14:07
                        • Ответ был помечен как решение.

                        Мне ещё одна мысль в голову пришла, попробуйте связаться с автором библиотеки.
                        Вроде адекватный малый, я ему в 2016-м году исправление посылал. Он быстро среагировал.
                        Может у него есть готовый пример для загрузки файла. Естественно писать ему нужно на английском.

                          Михаиллл
                          • 15 мая 2020 г. 14:22

                          Написал ему. А как долго он вам отвечал? И как думаете, в какую сторону кода стоит копать?

                            Evgenii Legotckoi
                            • 15 мая 2020 г. 14:26

                            Тогда он мне ответил вроде бы в течение пары дней.
                            Вполне возможно, что нужно правильно сформировать данные с изображением.
                            Также всё-таки может быть проблема со стороны сервера...
                            Но у меня не было никогда задачи делать загрузку изображения через QNetworkAccessManager и тем более делать этот на сервер с QtWebApp.
                            Поэтому я только предполагал возможные проблемы, но видимо там есть какой-то нюанс, который я не могу понять.

                              Михаиллл
                              • 16 мая 2020 г. 14:23

                              Скажите пожалуйста, как с помощью отладчика понять, загружен ли .ini файл?

                                Михаиллл
                                • 16 мая 2020 г. 15:11
                                • (ред.)

                                Оказалось в startup.cpp нужно в

                                1. /** Name of this application */
                                2. #define APPNAME "HttpSerwerQtWebAppGameTamada"

                                не забыть поменять имя, тогда инициализация проходит и сервер нормально работает

                                  Комментарии

                                  Только авторизованные пользователи могут публиковать комментарии.
                                  Пожалуйста, авторизуйтесь или зарегистрируйтесь