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, в который будут помещены данные из сохранённого файла.

widget.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());
}

donwloader.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 г. 0:32

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

Zeland
  • 28 февраля 2019 г. 16:12

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

Evgenii Legotckoi
  • 1 марта 2019 г. 5:46

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

F
  • 19 сентября 2019 г. 5:45

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

Evgenii Legotckoi
  • 25 сентября 2019 г. 3:53

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

ИП
  • 26 сентября 2019 г. 5:25

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

F
  • 26 сентября 2019 г. 9:28
  • (ред.)

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

Андрей madmentat
  • 26 сентября 2022 г. 11:23
  • (ред.)

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

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

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

Evgenii Legotckoi
  • 27 сентября 2022 г. 8:41

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

g
  • 12 ноября 2023 г. 10:35

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

Комментарии

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

Qt - Тест 001. Сигналы и слоты

  • Результат:47баллов,
  • Очки рейтинга-6
A
  • Alena
  • 19 января 2025 г. 22:41

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

  • Результат:58баллов,
  • Очки рейтинга-2
OI
  • Ora Iro
  • 24 декабря 2024 г. 17:38

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

  • Результат:40баллов,
  • Очки рейтинга-8
Последние комментарии
ИМ
Игорь Максимов22 ноября 2024 г. 22:51
Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi1 ноября 2024 г. 0:37
Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 октября 2024 г. 18:19
Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5 октября 2024 г. 17:51
Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55 июля 2024 г. 21:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Сейчас обсуждают на форуме
n
nkly3 января 2025 г. 13:52
Нужно запретить перемещение только некоторых итемов, остальные перемещать можно. Вопрос решен. Узнать QModelIndex элемента на который мы перетаскиваем другой элемент, можно с помощью функции indexAt(event->position().toPoint()) представления QTreeViev вызываемой в переопр…
M
Marsel17 августа 2023 г. 0:26
OAuth2.0 через VK, получение email Спасибо большое за помощь и простите за то что отнял время своей невнимательностью.
Evgenii Legotckoi
Evgenii Legotckoi25 июня 2024 г. 1:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 ноября 2024 г. 17:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject4 июня 2022 г. 13:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…

Следите за нами в социальных сетях