Evgenii Legotckoi
Evgenii Legotckoi13 грудня 2015 р. 10: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.

#-------------------------------------------------
#
# Project created by QtCreator 2015-12-13T21:02:32
#
#-------------------------------------------------

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = DownloadHttp
TEMPLATE = app


SOURCES += main.cpp\
        widget.cpp \
    downloader.cpp

HEADERS  += widget.h \
    downloader.h

FORMS    += widget.ui

widget.ui

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

віджет.h

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

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QFile>

#include <downloader.h>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void readFile();

private:
    Ui::Widget *ui;
    Downloader *downloader; // Объявляем объект класса для скачивания данных по http
};

#endif // WIDGET_H

widget.cpp

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

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    downloader = new Downloader(); // Инициализируем Downloader

    // по нажатию кнопки запускаем получение данных по http
    connect(ui->pushButton, &QPushButton::clicked, downloader, &Downloader::getData);
    // по окончанию получения данных считываем данные из файл
    connect(downloader, &Downloader::onReady, this, &Widget::readFile);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::readFile()
{
    QFile file("C:/example/file.xml");
    if (!file.open(QIODevice::ReadOnly)) // Открваем файл, если это возможно
            return; // если открытие файла невозможно, выходим из слота
    // в противном случае считываем данные и устанавилваем их в textEdit
    ui->textEdit->setText(file.readAll());
}

downloader.h

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

#ifndef DOWNLOADER_H
#define DOWNLOADER_H

#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QFile>
#include <QUrl>
#include <QDebug>

class Downloader : public QObject
{
    Q_OBJECT
public:
    explicit Downloader(QObject *parent = 0);

signals:
    void onReady();

public slots:
    void getData();     // Метод инициализации запроса на получение данных
    void onResult(QNetworkReply *reply);    // Слот обработки ответа о полученных данных

private:
    QNetworkAccessManager *manager; // менеджер сетевого доступа
};

#endif // DOWNLOADER_H

downloader.cpp

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

#include "downloader.h"

Downloader::Downloader(QObject *parent) : QObject(parent)
{
    // Инициализируем менеджер ...
    manager = new QNetworkAccessManager();
    // ... и подключаем сигнал о завершении получения данных к обработчику полученного ответа
    connect(manager, &QNetworkAccessManager::finished, this, &Downloader::onResult);
}

void Downloader::getData()
{
    QUrl url("http://www.mtbank.by/currxml.php"); // URL, к которому будем получать данные
    QNetworkRequest request;    // Отправляемый запрос
    request.setUrl(url);        // Устанавлвиваем URL в запрос
    manager->get(request);      // Выполняем запрос
}

void Downloader::onResult(QNetworkReply *reply)
{
    // Если в процесе получения данных произошла ошибка
    if(reply->error()){
        // Сообщаем об этом и показываем информацию об ошибках
        qDebug() << "ERROR";
        qDebug() << reply->errorString();
    } else {
        // В противном случае создаём объект для работы с файлом
        QFile *file = new QFile("C:/example/file.xml");
        // Создаём файл или открываем его на перезапись ...
        if(file->open(QFile::WriteOnly)){
            file->write(reply->readAll());  // ... и записываем всю информацию со страницы в файл
            file->close();                  // закрываем файл
        qDebug() << "Downloading is completed";
        emit onReady(); // Посылаем сигнал о завершении получения файла
        }
    }
}

Підсумок

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

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

Відеоурок

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

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

C
  • 21 квітня 2017 р. 13: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 р. 13:40

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

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

C
  • 21 квітня 2017 р. 14: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
  • 21 квітня 2017 р. 14: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
  • 21 квітня 2017 р. 14: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 р. 00:32

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

Zeland
  • 28 лютого 2019 р. 16:12

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
d
  • dsfs
  • 26 квітня 2024 р. 01:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
d
  • dsfs
  • 26 квітня 2024 р. 01:45

C++ - Тест 002. Константы

  • Результат:50бали,
  • Рейтинг балів-4
d
  • dsfs
  • 26 квітня 2024 р. 01:35

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

  • Результат:73бали,
  • Рейтинг балів1
Останні коментарі
k
kmssr08 лютого 2024 р. 15:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко04 лютого 2024 р. 22:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 07:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 05:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik18 грудня 2023 р. 18:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi02 травня 2024 р. 11:07
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.
IscanderChe
IscanderChe30 квітня 2024 р. 01:22
Во Flask рендер шаблона не передаётся в браузер Доброе утро! Имеется вот такой шаблон: <!doctype html><html> <head> <title>{{ title }}</title> <link rel="stylesheet" href="{{ url_…
G
Gar22 квітня 2024 р. 02:46
Clipboard Как скопировать окно целиком в clipb?
Павел Дорофеев
Павел Дорофеев13 квітня 2024 р. 23:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex04 квітня 2024 р. 01:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Слідкуйте за нами в соціальних мережах