Evgenii Legotckoi
13 грудня 2015 р. 21:58

Qt/C++ - Урок 035. Завантаження файлів через HTTP за допомогою QNetworkAccessManager

Для роботи з мережею крім використання класів QTcpSocket або QUdpSocket можна використовувати QNetworkAccessManager. Цей клас надає функціонал для надсилання запитів по мережі та отримання відповідей та зручний для роботи з протоколом HTTP.

Тому пропоную написати програму, яка дозволить завантажити xml-файл із сайту та записати його файл на локальному диску комп'ютера.

Логіка програми наступна:

  1. Завантажити файл;
  2. Записати його на локальний диск наступним шляхом C:/example/file.xml;
  3. Прочитати записаний файл та відобразити дані у QTextEdit.

Структура проекту для роботи з HTTP

Структура проекту така:

  • DownloadHttp.pro - профайл проекту;
  • main.cpp - основний файл вихідних кодів програми;
  • widget.h - заголовний файл вікна програми;
  • widget.cpp - файл вихідних кодів вікна програми;
  • downloader.h - заголовний файл класу для завантаження файлу;
  • doqnloader.cpp - файл вихідних кодів класу для завантаження файлу.

DownloadHttp.pro і main.cpp

main.cpp створюється за замовчуванням і не модифікується, тоді як у файлі DownloadHttp.pro необхідно підключити модуль network.

  1. #-------------------------------------------------
  2. #
  3. # Project created by QtCreator 2015-12-13T21:02:32
  4. #
  5. #-------------------------------------------------
  6.  
  7. QT += core gui network
  8.  
  9. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
  10.  
  11. TARGET = DownloadHttp
  12. TEMPLATE = app
  13.  
  14.  
  15. SOURCES += main.cpp\
  16. widget.cpp \
  17. downloader.cpp
  18.  
  19. HEADERS += widget.h \
  20. downloader.h
  21.  
  22. FORMS += widget.ui

widget.ui

У вікні програми розташовується кнопка, за натисканням якої буде здійснюватися запуск скачування даних із сайту та QTextEdit, в який будуть розміщені дані зі збереженого файлу.

віджет.h

Заголовний файл вікна програми. У ньому ми підключимо заголовний файл класу Downloader, який відповідатиме за скачування даних із сайту та збереження їх у файл. Природно оголосимо об'єкт даного класу Downloader. Також є оголошення сигнатури слота для читання даних із збереженого файлу по закінченню скачування.

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3.  
  4. #include <QWidget>
  5. #include <QFile>
  6.  
  7. #include <downloader.h>
  8.  
  9. namespace Ui {
  10. class Widget;
  11. }
  12.  
  13. class Widget : public QWidget
  14. {
  15. Q_OBJECT
  16.  
  17. public:
  18. explicit Widget(QWidget *parent = 0);
  19. ~Widget();
  20.  
  21. private slots:
  22. void readFile();
  23.  
  24. private:
  25. Ui::Widget *ui;
  26. Downloader *downloader; // Объявляем объект класса для скачивания данных по http
  27. };
  28.  
  29. #endif // WIDGET_H

widget.cpp

У вихідних кодах присутні два конекти сигналів до слотів. Один коннект відповідає за обробку натискання кнопки, а другий за читання даних із файлу по закінченню завантаження файлу.

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3.  
  4. Widget::Widget(QWidget *parent) :
  5. QWidget(parent),
  6. ui(new Ui::Widget)
  7. {
  8. ui->setupUi(this);
  9.  
  10. downloader = new Downloader(); // Инициализируем Downloader
  11.  
  12. // по нажатию кнопки запускаем получение данных по http
  13. connect(ui->pushButton, &QPushButton::clicked, downloader, &Downloader::getData);
  14. // по окончанию получения данных считываем данные из файл
  15. connect(downloader, &Downloader::onReady, this, &Widget::readFile);
  16.  
  17. }
  18.  
  19. Widget::~Widget()
  20. {
  21. delete ui;
  22. }
  23.  
  24. void Widget::readFile()
  25. {
  26. QFile file("C:/example/file.xml");
  27. if (!file.open(QIODevice::ReadOnly)) // Открваем файл, если это возможно
  28. return; // если открытие файла невозможно, выходим из слота
  29. // в противном случае считываем данные и устанавилваем их в textEdit
  30. ui->textEdit->setText(file.readAll());
  31. }

downloader.h

А тепер заголовний файл самого винуватця урочистості. У ньому Ми оголошуємо екземпляр класу QNetworkAccesManager, а також методи для ініціалізації запиту до сайту з його URL та обробки отриманої відповіді.

  1. #ifndef DOWNLOADER_H
  2. #define DOWNLOADER_H
  3.  
  4. #include <QObject>
  5. #include <QNetworkAccessManager>
  6. #include <QNetworkRequest>
  7. #include <QNetworkReply>
  8. #include <QFile>
  9. #include <QUrl>
  10. #include <QDebug>
  11.  
  12. class Downloader : public QObject
  13. {
  14. Q_OBJECT
  15. public:
  16. explicit Downloader(QObject *parent = 0);
  17.  
  18. signals:
  19. void onReady();
  20.  
  21. public slots:
  22. void getData(); // Метод инициализации запроса на получение данных
  23. void onResult(QNetworkReply *reply); // Слот обработки ответа о полученных данных
  24.  
  25. private:
  26. QNetworkAccessManager *manager; // менеджер сетевого доступа
  27. };
  28.  
  29. #endif // DOWNLOADER_H

downloader.cpp

УВАГА!!! - Перевірте доступність URL з прикладу перед запуском програми, щоб не битися головою об стіну, якщо сайт просто недоступний. А краще замініть на ту URL-адресу, до якої Ви бажаєте достукатися. А також можна змінити розширення файлу на txt.

  1. #include "downloader.h"
  2.  
  3. Downloader::Downloader(QObject *parent) : QObject(parent)
  4. {
  5. // Инициализируем менеджер ...
  6. manager = new QNetworkAccessManager();
  7. // ... и подключаем сигнал о завершении получения данных к обработчику полученного ответа
  8. connect(manager, &QNetworkAccessManager::finished, this, &Downloader::onResult);
  9. }
  10.  
  11. void Downloader::getData()
  12. {
  13. QUrl url("http://www.mtbank.by/currxml.php"); // URL, к которому будем получать данные
  14. QNetworkRequest request; // Отправляемый запрос
  15. request.setUrl(url); // Устанавлвиваем URL в запрос
  16. manager->get(request); // Выполняем запрос
  17. }
  18.  
  19. void Downloader::onResult(QNetworkReply *reply)
  20. {
  21. // Если в процесе получения данных произошла ошибка
  22. if(reply->error()){
  23. // Сообщаем об этом и показываем информацию об ошибках
  24. qDebug() << "ERROR";
  25. qDebug() << reply->errorString();
  26. } else {
  27. // В противном случае создаём объект для работы с файлом
  28. QFile *file = new QFile("C:/example/file.xml");
  29. // Создаём файл или открываем его на перезапись ...
  30. if(file->open(QFile::WriteOnly)){
  31. file->write(reply->readAll()); // ... и записываем всю информацию со страницы в файл
  32. file->close(); // закрываем файл
  33. qDebug() << "Downloading is completed";
  34. emit onReady(); // Посылаем сигнал о завершении получения файла
  35. }
  36. }
  37. }

Підсумок

В результаті ви отримаєте програму, яка скине дані зі сторінки сайту і відобразить з QTextEdit, як показано на наступному малюнку. Демонстрація роботи програми доступна у відеоуроку.

Посилання на завантаження проекту в zip-архіві: downloadhttp.zip

Відеоурок

Вам це подобається? Поділіться в соціальних мережах!

C
  • 21 квітня 2017 р. 23:18

Здравствуйте! Когда тестировал ваш проект в режиме Debug выводит такую ошибку:

-1: error: Exception at 0x74f3b782, code: 0xd: , flags=0x1 (execution cannot be continued) (first chance)
после закрытия программы. Также и с моим проектом. Я искал ошибку и нашел ошибку вот в этой строке: manager = new QNetworkAccessManager(); Также и у меня когда создаю обект класса QNetworkAccessManager. В чем пожет быть проблема? Спасибо.
Evgenii Legotckoi
  • 21 квітня 2017 р. 23:40

Здравствуйте! Не знаю. На всякий случай я скачал проект и проверил сейчас у себя его работу, но никаких исключений не выкидывает. Проверял на Qt 5.8 GCC 64 bit под Ubuntu 16.04. Проект работает стабильно.

Какой версией Qt пользуетесь?

C
  • 22 квітня 2017 р. 00:08

Я использую Qt 5.7.1 с Microsoft Visual Studio 2015 (Windows 10) и на Qt 5.9.0 Beta 2 также. У меня мой и ваш проект работает стабильно, но если через Debug тестировать и закрыть программу то такая ошибка появляется. Пробовал через Visual Studio 2015 отлаживать, но выводит ошибку:

Exception thrown at 0x74CCB782 (KernelBase.dll) in MyTestApp.exe: 0x0000071A: The remote procedure call was canceled, or if a call time-out was specified, the call timed out. If there is a handler for this exception, the program may be safely continued.
Или иногда эту ошибку:
Exception thrown at 0x74CCB782 (KernelBase.dll) in MyTestApp.exe: 0x0000000D: The data is invalid.
После выводит код ассемблера:
74CCB782 mov ecx,dword ptr [esp+54h]
К примеру на Windows 8.1 x64 тестировал то такую ошибку выводит как предупреждение: -1: error: Exception at 0x74f3b782, code: 0xd: , flags=0x1 (execution cannot be continued) (first chance) А на Windows 10 как ошибку. Не могу точно найти где ошибка, но если закоментировать код инициализации QNetworkAccessManager: manager = new QNetworkAccessManager(); тогда ошибки нет и соотвественно не работает подключение сети в программе.
Evgenii Legotckoi
  • 22 квітня 2017 р. 00:33

Не похоже, чтобы это была проблема с Qt или ошибкой в коде. У меня такой проблемы как у вас не возникает. Проверил на следующем:

  1. Ubuntu 16.04 - GCC 64 bit - Qt5.8
  2. Windows 7 - MinGIW 32 bit - Qt5.7
  3. Windows 7 - MSVC 2015 - Qt5.7

Исключения по окончании работы не выбрасываются. У вас фигурирует в исключениях KernelBase.dll . Скорее всего это или придурь компилятора или сама библиотека кривая.

Но для очистки совести могу посоветовать передать указатель на parent объект при создании объекта QNetworkAccessManager

manager = new QNetworkAccessManager(this);
C
  • 22 квітня 2017 р. 00:57

Да, и если указать parent (manager = new QNetworkAccessManager(this);) также ошибка. Я вот что еще заметил когда тестировал на Visual Studio 2015, Thread: [1328]wlanapi.dll!_NotificationApcThreadProc@4, то есть ошибка/сбой программы в этой функции, но у меня нет подключения к wlanapi (библиотеки WiFi) в программе, значит ошибка происходит только на Windows 10 и с библиотекой Qt (Qt5Networkd.dll/Qt5Network.dll).

Evgenii Legotckoi
  • 22 квітня 2017 р. 10:32

Считаю, что библиотека Qt Network работает нормально. Дело в том, что данная библиотека перебирает все возможные варианты подключений, в том числе и wi-fi подключения, когда пытается найти выход в интернет. Мне думается, что тут исключительно локальная проблема с системными библиотеками на Windows 10.

Zeland
  • 01 березня 2019 р. 03:12

Евгений, спасибо за полезную статью. Немного отредактировал добавленные к уроку файлы, чтобы запустить программу на версии фреймворма Qt 5.10.
DownloadHttp.zip DownloadHttp.zip

Evgenii Legotckoi
  • 01 березня 2019 р. 16:46

Вам спасибо за дополнение.

F
  • 19 вересня 2019 р. 15:45

А вот как выгрузить файл на сервер по http протоколу? Допустим на regRu. И как получить путь файла, которой отображается в файловом менеджере regRu, чтобы загрузить его.

Evgenii Legotckoi
  • 25 вересня 2019 р. 13:53

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

ИП
  • 26 вересня 2019 р. 15:25

Как произвести загрузку используя HTTPS протокол?

F
  • 26 вересня 2019 р. 19:28
  • (відредаговано)

точно так же как и http. Просто url адрес будет с https.

Андрей madmentat
  • 26 вересня 2022 р. 21:23
  • (відредаговано)

Здравствуйте! Подскажите, пожалуйста, как сделать так, чтобы программа срабатыала без нажатия кнопки? Ну чисто при загрузке формы... Я так понимаю, надо что-то поменять в этой строчке

  1. connect(ui->pushButton, &QPushButton::clicked, downloader, &Downloader::getData);
  2.  

Но вот что именно? Если не сложно, свяжитесь со мной. А то я спать спокойно не смогу пока не выясню как это делается.

Evgenii Legotckoi
  • 27 вересня 2022 р. 18:41

Попробуйте просто вызвать метод getData в конструкторе класса

g
  • 12 листопада 2023 р. 21:35

Добрый день.
Изучаю Qt на ваших уроках. Всё нормально работает на Linux. А под Win один раз запустилось, а сейчас вместо данных сайта получается ошибк "Unable to write". Куда копать, ума не приложу.
Кто подскажет, буду благдарен.

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
  • Останні коментарі
  • Evgenii Legotckoi
    16 квітня 2025 р. 17:08
    Благодарю за отзыв. И вам желаю всяческих успехов!
  • IscanderChe
    12 квітня 2025 р. 17:12
    Добрый день. Спасибо Вам за этот проект и отдельно за ответы на форуме, которые мне очень помогли в некоммерческих пет-проектах. Профессиональным программистом я так и не стал, но узнал мно…
  • AK
    01 квітня 2025 р. 11:41
    Добрый день. В данный момент работаю над проектом, где необходимо выводить звук из программы в определенное аудиоустройство (колонки, наушники, виртуальный кабель и т.д). Пишу на Qt5.12.12 поско…
  • Evgenii Legotckoi
    09 березня 2025 р. 21:02
    К сожалению, я этого подсказать не могу, поскольку у меня нет необходимости в обходе блокировок и т.д. Поэтому я и не задавался решением этой проблемы. Ну выглядит так, что вам действитель…
  • VP
    09 березня 2025 р. 16:14
    Здравствуйте! Я устанавливал Qt6 из исходников а также Qt Creator по отдельности. Все компоненты, связанные с разработкой для Android, установлены. Кроме одного... Когда пытаюсь скомпилиров…