14 мая 2020 г. 8:41

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

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

413 Entity too large

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

void AppCore::slotApiRequest(QByteArray data)
{
    QNetworkRequest request;
    request.setHeader( QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded" );
    request.setUrl(QUrl(urlServerForRequest));
    request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); //разрешает перенаправление
    request.setHeader(QNetworkRequest::ContentTypeHeader,"application/json");
    request.setHeader(QNetworkRequest::ContentLengthHeader,QByteArray::number(data.size()));
    myApiQuery.post(request, data);
}

void AppCore::addCategory(QString nameCategory, QString fileName)
{
    fileName = fileName.remove("file:///");
    //qDebug()<<nameCategory<<fileName;
    QImage myImage(fileName);

    QByteArray bArray;
    QBuffer buffer(&bArray);
    buffer.open(QIODevice::WriteOnly);
    myImage.save(&buffer, "BMP");
    QString imageString("data:image/bmp;base64,");
    imageString.append(QString::fromLatin1(bArray.toBase64().data()));
    //qDebug()<<imageString;

    QJsonObject jObj = QJsonObject{{"nameCategory", nameCategory}, {"image", imageString}};
    //QJsonObject jObj = QJsonObject{{"nameCategory", nameCategory}, {"image", fileName}};
    QJsonDocument doc = QJsonDocument(jObj);
    QByteArray arrayJson;
    QString strJson(doc.toJson(QJsonDocument::Compact));
    arrayJson += strJson;

    urlServerForRequest= urlServer + "addCategory";
    slotApiRequest(arrayJson);
}
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
16

Добрый день.

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

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

client_max_body_size 2m;

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

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

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

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

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

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

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

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

[listener]
; host=192.168.0.100
port=8080
minThreads=4
maxThreads=100
cleanupInterval=60000
readTimeout=60000
maxRequestSize=10000000
maxMultiPartSize=10000000

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

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

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

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

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

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

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

request.setHeader( QNetworkRequest::ContentTypeHeader, "multipart/form-data" );

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

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

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

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

Сделал так

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

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

"413 Entity too large\r\n"

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

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

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

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

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

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

void HttpConnectionHandler::read()
 {
     // The loop adds support for HTTP pipelinig
     while (socket->bytesAvailable())
     {
         #ifdef SUPERVERBOSE
             qDebug("HttpConnectionHandler (%p): read input",static_cast<void*>(this));
         #endif

         // Create new HttpRequest object if necessary
         if (!currentRequest)
         {
             currentRequest=new HttpRequest(settings);
         }

         // Collect data for the request object
         while (socket->bytesAvailable() && currentRequest->getStatus()!=HttpRequest::complete && currentRequest->getStatus()!=HttpRequest::abort)
         {
             currentRequest->readFromSocket(socket);
             if (currentRequest->getStatus()==HttpRequest::waitForBody)
             {
                 // Restart timer for read timeout, otherwise it would
                 // expire during large file uploads.
                 int readTimeout=settings->value("readTimeout",10000).toInt();
                 readTimer.start(readTimeout);
             }
         }

         // If the request is aborted, return error message and close the connection
         if (currentRequest->getStatus()==HttpRequest::abort)
         {
             socket->write("HTTP/1.1 413 entity too large\r\nConnection: close\r\n\r\n413 Entity too large\r\n");
             while(socket->bytesToWrite()) socket->waitForBytesWritten();
             socket->disconnectFromHost();
             delete currentRequest;
             currentRequest=nullptr;
             return;
         }

         // If the request is complete, let the request mapper dispatch it
         if (currentRequest->getStatus()==HttpRequest::complete)
         {
             readTimer.stop();
             qDebug("HttpConnectionHandler (%p): received request",static_cast<void*>(this));

             // Copy the Connection:close header to the response
             HttpResponse response(socket);
             bool closeConnection=QString::compare(currentRequest->getHeader("Connection"),"close",Qt::CaseInsensitive)==0;
             if (closeConnection)
             {
                 response.setHeader("Connection","close");
             }

             // In case of HTTP 1.0 protocol add the Connection:close header.
             // This ensures that the HttpResponse does not activate chunked mode, which is not spported by HTTP 1.0.
             else
             {
                 bool http1_0=QString::compare(currentRequest->getVersion(),"HTTP/1.0",Qt::CaseInsensitive)==0;
                 if (http1_0)
                 {
                     closeConnection=true;
                     response.setHeader("Connection","close");
                 }
             }

             // Call the request mapper
             try
             {
                 requestHandler->service(*currentRequest, response);
             }
             catch (...)
             {
                 qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler",
                           static_cast<void*>(this));
             }

             // Finalize sending the response if not already done
             if (!response.hasSentLastPart())
             {
                 response.write(QByteArray(),true);
             }

             qDebug("HttpConnectionHandler (%p): finished request",static_cast<void*>(this));

             // Find out whether the connection must be closed
             if (!closeConnection)
             {
                 // Maybe the request handler or mapper added a Connection:close header in the meantime
                 bool closeResponse=QString::compare(response.getHeaders().value("Connection"),"close",Qt::CaseInsensitive)==0;
                 if (closeResponse==true)
                 {
                     closeConnection=true;
                 }
                 else
                 {
                     // If we have no Content-Length header and did not use chunked mode, then we have to close the
                     // connection to tell the HTTP client that the end of the response has been reached.
                     bool hasContentLength=response.getHeaders().contains("Content-Length");
                     if (!hasContentLength)
                     {
                         bool hasChunkedMode=QString::compare(response.getHeaders().value("Transfer-Encoding"),"chunked",Qt::CaseInsensitive)==0;
                         if (!hasChunkedMode)
                         {
                             closeConnection=true;
                         }
                     }
                 }
             }

             // Close the connection or prepare for the next request on the same connection.
             if (closeConnection)
             {
                 while(socket->bytesToWrite()) socket->waitForBytesWritten();
                 socket->disconnectFromHost();
             }
             else
             {
                 // Start timer for next request
                 int readTimeout=settings->value("readTimeout",10000).toInt();
                 readTimer.start(readTimeout);
             }
             delete currentRequest;
             currentRequest=nullptr;
         }
     }
 }

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

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

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

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

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

/** Name of this application */
#define APPNAME "HttpSerwerQtWebAppGameTamada"

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

Комментарии

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

Внесите вклад в развитие сообщества EVILEG.

Узнайте, как стать автором сайта.

Изучить
Donate

Добрый день, Дорогие Пользователи !!!

Я Евгений Легоцкой, разработчик EVILEG. И это мой хобби-проект, который помогает учиться программированию другим программистам и разработчикам

Если сайт помог вам, и вы хотите также поддержать развитие сайта, то вы можете сделать пожертвование следующими способами

PayPalYandex.Money
Timeweb

Позвольте мне порекомендовать вам отличный хостинг, на котором расположен EVILEG.

В течение многих лет Timeweb доказывает свою стабильность.

Для проектов на Django рекомендую VDS хостинг

Посмотреть Хостинг Timeweb
s
3 июня 2020 г. 2:56
silo1995

C++ - Тест 003. Условия и циклы

  • Результат:35баллов,
  • Очки рейтинга-10
АП
2 июня 2020 г. 22:11
Алексей Пикенин

C++ - Тест 005. Структуры и Классы

  • Результат:75баллов,
  • Очки рейтинга2
2 июня 2020 г. 14:04
Даниил Чижевский

C++ - Тест 001. Первая программа и типы данных

  • Результат:86баллов,
  • Очки рейтинга6
Последние комментарии
5 июня 2020 г. 2:39
Евгений Легоцкой

Qt/C++ - Урок 091. Как написать кастомный делегат управляющий подсветкой строки в таблице

По-моему, смысла в этом нет особого. Если делегат будет игнорировать настройки таблицы, то это приведёт ещё к большему непониманию, что вообще происходит, для программиста, который после вас буд…
5 июня 2020 г. 2:34
IscanderChe

Qt/C++ - Урок 091. Как написать кастомный делегат управляющий подсветкой строки в таблице

Сижу, размышляю: можно ли переписать делегата так, чтобы независимо от настроек строк выделялись строки?
5 июня 2020 г. 2:31
Евгений Легоцкой

Qt/C++ - Урок 091. Как написать кастомный делегат управляющий подсветкой строки в таблице

Понятно. Я не обратил внимания на то, что там было в старом коде по настройкам строк :)
5 июня 2020 г. 2:27
IscanderChe

Qt/C++ - Урок 091. Как написать кастомный делегат управляющий подсветкой строки в таблице

Разобрался. У вас изначально в проекте были вот эти настройки: ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);ui->tableView->setSelectionMode(QAbstractItemVie…
4 июня 2020 г. 12:10
IscanderChe

Qt/C++ - Урок 091. Как написать кастомный делегат управляющий подсветкой строки в таблице

Полностью скопировал пример - всё правильно работает. Значит, где-то у меня ошибки в тестовом проекте. Буду разбираться. Извините за беспокойство. :)
Сейчас обсуждают на форуме
МА
f
3 июня 2020 г. 2:49
fryn3

Можно ли сделать в QML таблицу как в Excel?

edi-tableview - нашел пока такое выглядит коряво, посмотрим что можно сделать
2 июня 2020 г. 3:46
Евгений Легоцкой

Медиа файлы Google Firebase

Картинки можете попробовать сжимать через QPixmap, там есть возможность установки scaleFactor, через него можете устанавливать нужные параметры. А что касается конвертации видео, то лучше п…
2 июня 2020 г. 3:01
Евгений Легоцкой

Перехват обращения к локальным файлам QWebEngineView

В вашем случае вполне адекватное решение. Так сказать меньше зло. В противном случае пришлось бы очень много переписывать и перепиливать.
a
1 июня 2020 г. 11:26
alekseyttrv

SSL на Android

у меня стоит версия Qt 5.14.2. В настройках android поставил openssl из коробки, и этот прроект автоматически стянулся. Достаточно было только добавить в .pro-файл строку после этого и все …
О нас
Услуги
© EVILEG 2015-2020
Рекомендует хостинг TIMEWEB