Евгений Легоцкой13 декабря 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 хостинг.
Поддержать автора Donate
C

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

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

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

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

C

Я использую 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(); тогда ошибки нет и соотвественно не работает подключение сети в программе.

Не похоже, чтобы это была проблема с 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

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

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

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

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

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

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

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

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

ИП

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

F

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

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

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

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

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

Комментарии

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

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

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

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

Посмотреть Хостинг
YL

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

  • Результат:66баллов,
  • Очки рейтинга-1
k
  • korsar
  • 23 ноября 2022 г. 9:57

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

  • Результат:50баллов,
  • Очки рейтинга-4
TM

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

  • Результат:78баллов,
  • Очки рейтинга2
Последние комментарии
b

GameDev на Qt - Урок 5. Взрыв от пуль с помощью sprite картинки

Если вдруг кто-то прочитает.... Скачал проект, скомпилил, запустил. Всё красиво и объектно ориентировано, но вот FPS дико страдает, когда появляется 10+ врагов. Может есть какие-то надстрой…
  • juvf
  • 25 ноября 2022 г. 1:14

Qt/C++ - Урок 051. QMediaPlayer - Аудио плеер на Qt

Добрый день. Подскажите, как можно перехватить в Qt или Qml уровень воспроизводимого звука? Т.е. требуется сделать виртуальный винтажный индикатор (стрелочный или светодиодный), который бы …
  • juvf
  • 3 ноября 2022 г. 4:20

QML - Урок 007. ListView Qml. Динамическое создание и удаление элементов

Добрый день. Очень полезная статья. Спасибо. Вопрос такой: 1) нужно "взять" кнопку 2 пальцем (прикаснулись пальцем к кнопке 2, держим, через 2-3 кнопка оторвалась от ListView) и пере…

PyQt5 - Урок 007. Работаем с QML QtQuick (Сигналы и слоты)

А можно ли из QML сделать привязку свойства к свойству пайтоновского объекта? Ну, т.е. , например, у нашего объекта Calculator обхвялем свойства sumresult и subresult c с декоратором @pyqtProp…
МА

Распознавание изображений на Python с помощью TensorFlow и Keras

А что собственно выводит программа, как вывести то что она смогла распознать?
Сейчас обсуждают на форуме
АБ

Sorting the added QML elements in the ListModel

I am writing an alarm clock in QML, I am required to sort the alarms in ascending order (depending on the date or time (if there are several alarms on the same day). I've done the sorting …
АМ

Как добавить в скрипт размер каждого файла в Мб ?

IDLE (Python 3.10 64-bit) Win 10 Есть такой скрипт: Поиск перечня файлов в папке и запись списка: import ospath = 'E:\Мой Python\__Видеокурсы Python'rez = sorted(os.listdir(pa…
n
  • nkly
  • 18 октября 2022 г. 10:14

Как сохранить данные древовидной модели на основе QStandardItemModel в файл

Вы меня неправильно поняли. Допустим я собираю кулинарные рецепты и один из них - рецепт супа Харчо. Структура файла данных такова: { node:Рецепт супа Харчо parent:Вкусные блюда, …

Вопрос по Qt Creator

Добрый день. Не знаю, подобную проблему я не решал.

Задать другой класс div-у

Добрый день. Попробуйте использовать Selenium. Это библиотека есть в виде Python модуля и она позволяет загружать страницу и манипулировать html элементами. Как я понимаю, в ней можно…
О нас
Услуги
© EVILEG 2015-2022
Рекомендует хостинг TIMEWEB