Реклама

Qt/C++ - Урок 035. Скачивание файла по HTTP с помощью QNetworkAccessManager

РуководствоQtQNetworkAccessManager, Qt, http, скачивание файла1471

Для работы с сетью кроме использования классов 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

Видеоурок

Реклама
Реклама

Комментарии

Здравствуйте! Когда тестировал ваш проект в режиме 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 пользуетесь?

Я использую 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);

Да, и если указать 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.

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
Реклама
Последние комментарии
  • EVILEG
  • 24 апреля 2017 г. 20:44
Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

У меня пока мыслей на этот счёт нет ((

Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

Пробовал играться с шарком, либо я криво смотрел, либо почему-то POST запросы на oauth.yandex.ru не летят, хотя должны постом лететь, я и исходники QOAuth2AuthorizationCodeFlow ковырял на пред

  • EVILEG
  • 24 апреля 2017 г. 13:39
Подключение вашего Qt приложения к сервисам Google, используя OAuth 2.0

Возможно, стоит Wireshark`ом запросы посмотреть. В чём отличие идёт аякс запроса от запроса из библиотеки. Возможно, что не хватает какой-нибудь заголовочной информации.

Сейчас обсуждают на форуме
  • EVILEG
  • 28 апреля 2017 г. 20:02
Qt Installer Framework. Вызов программы при деинсталляции.

А. Вон оно как. Ну хорошо, что оказалось так просто.

  • EVILEG
  • 28 апреля 2017 г. 17:01
QTWebEngineView

На самом деле всё гораздо проще. у QWebEngineView есть сигнал urlChanged() . Вот его и нужно использовать, чтобы получить новый url страницы. Заголовочный файл #...

  • EVILEG
  • 28 апреля 2017 г. 9:30
Другой ToolBar

Если ToolBar все должны находиться внутри окна mainwindow, то создайте необходимое количество ToolBar`ов и часть из них сделайте скрытыми. А когда открываете диалог, то показывайте скрытые Too...

  • EVILEG
  • 28 апреля 2017 г. 9:13
Ошибка

Заголовочный файл класса StyleHelper не подключили в файл реализации mainwindow.cpp

Реклама