QtNetwork: Download xml file and read its content - c++

I have a Qt application where I am trying to download a XML file form a server and then read the content of the file.
Unfortunately, I am not able to get the content of the downloaded file in to a QDomDocument.
This is what I tried
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileIsReady(QNetworkReply*)) );
manager->get(QNetworkRequest(QUrl("http://example.com/file.xml")));
fileIsReady(QNetworkReply *reply){
QTemporaryFile tempFile;
if(tempFile.open()){
tempFile.write(reply->readAll());
QDomDocument versionXML;
QDomElement root;
if(!versionXML.setContent(&tempFile)){
qDebug() << "failed to load version file" << endl;
}
else{
root=versionXML.firstChildElement();
//...
}
}
}
How can I achieve this?

I think the streaming interfaces are a bit hard to use when you are new to Qt. If you don't have super-big downloads that fit into RAM, just use QByteArray.
fileIsReady(QNetworkReply *reply){
QByteArray data = reply->readAll();
qDebug() << "XML download size:" << data.size() << "bytes";
qDebug() << QString::​fromUtf8(data);
QDomDocument versionXML;
if(!versionXML.setContent(data))
{
qWarning() << "Failed to parse XML";
}
// ...
}

Related

How to measure download percent using QNetwork?

In the example below sometimes the file is downloaded correctly, and sometimes, I'm getting these values in the qDebug() added into the downloadProgress lambda:
Percent complete: -1.08905e+09
Downloaded 11623330 of -1
And then the download fails, I mean it saves a zip file with 0 bytes.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkRequest request;
// Random link just to test:
request.setUrl(
QUrl("https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n5.0.2.zip"));
QNetworkReply *reply = manager->get(request);
connect(reply, &QNetworkReply::downloadProgress,
[this, reply](qint64 bytesReceived, qint64 bytesTotal)
{
qDebug() << "Downloaded " << bytesReceived << " of " << bytesTotal;
double percentComplete = (bytesReceived * 100.0) / bytesTotal;
qDebug() << "Percent complete: " << percentComplete;
});
connect(reply, &QNetworkReply::finished, [this, reply]()
{
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << "Error: " << reply->errorString();
} else
{
QString fileName = "C:/Users/Raja/Downloads/file.zip";
QFile file(fileName);
if (file.open(QIODevice::WriteOnly))
{
file.write(reply->readAll());
file.close();
qDebug() << "File downloaded successfully";
} else
qDebug() << "Error: Unable to open the file";
}
reply->deleteLater();
});
What i'm missing?
Did you read the documentation?
It says that bytesTotal is -1 if the total size is unknown and
The download is finished when bytesReceived is equal to bytesTotal. At that time, bytesTotal will not be -1.
In other words: That behavior is expected and just means the download is still in progress. This probably happens when the server doesn't send a content-length header. See What's the "Content-Length" field in HTTP header?

Qt 6.2: QMediaPlayer & QByteArray

Good day.
Has anyone tried QMediaPlayer in Qt 6.2 already?
I'm trying this code, but Media Status always remains as "NoMedia" and no any sound :).
Full test project: https://github.com/avttrue/MediaPlayerTest
#include "mainwindow.h"
#include <QDebug>
#include <QBuffer>
#include <QFile>
#include <QAudioOutput>
#include <QMediaPlayer>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QFile file("../test/Bankrobber.mp3");
if(!file.open(QIODevice::ReadOnly))
qDebug() << "File not opened";
qDebug() << "File size:" << file.size(); // File size: 11181085
QByteArray ba = file.readAll();
qDebug() << "ByteArray size:" << ba.size(); // ByteArray size: 11181085
QBuffer* buffer = new QBuffer(this);
buffer->setData(ba);
if(!buffer->open(QIODevice::ReadOnly))
qDebug() << "Buffer not opened";
qDebug() << "Buffer size:" << buffer->size(); // Buffer size: 11181085
buffer->seek(qint64(0));
auto audioOutput = new QAudioOutput(this);
auto player = new QMediaPlayer(this);
player->setAudioOutput(audioOutput);
audioOutput->setVolume(50);
player->setSourceDevice(buffer);
qDebug() << "Device:" << player->sourceDevice(); // Device: QBuffer(0x563180493020)
QObject::connect(player, &QMediaPlayer::mediaStatusChanged,
[=](QMediaPlayer::MediaStatus status)
{ qDebug() << "MediaStatus:" << player->mediaStatus() << "|" << status; });
QObject::connect(player, &QMediaPlayer::errorOccurred,
[=](QMediaPlayer::Error error)
{ qDebug() << "Error:" << player->errorString() << "|" << error; });
QObject::connect(player, &QMediaPlayer::playbackStateChanged,
[=](QMediaPlayer::PlaybackState state)
{ qDebug() << "PlaybackState:" << player->playbackState() << "|" << state; });
player->play();
qDebug() << "MediaStatus:" << player->mediaStatus(); // MediaStatus: QMediaPlayer::NoMedia
}
As the docs points out:
void QMediaPlayer::setSourceDevice(QIODevice *device, const QUrl
&sourceUrl = QUrl())
Sets the current source device.
The media data will be read from device. The sourceUrl can be provided
to resolve additional information about the media, mime type etc. The
device must be open and readable.
For macOS the device should also be seek-able.
Note: This function returns immediately after recording the specified
source of the media. It does not wait for the media to finish loading
and does not check for errors. Listen for the mediaStatusChanged() and
error() signals to be notified when the media is loaded, and if an
error occurs during loading.
(emphasis mine)
QMediaPlayer does not know how to deduce the file format so it does not load it. The solution is to point out that it is an mp3:
player->setSourceDevice(buffer, QUrl("foo.mp3"));
The function setSourceDevice() you are using isn't doing what you think?
Maybe you wanted setSource() instead?
Qt has great documentation:
https://doc.qt.io/qt-6/qmediaplayer.html#setSourceDevice
Even good examples:
player = new QMediaPlayer;
audioOutput = new QAudioOutput;
player->setAudioOutput(audioOutput);
connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
player->setSource(QUrl::fromLocalFile("/Users/me/Music/coolsong.mp3"));
audioOutput->setVolume(50);
player->play();
Ref.
https://doc.qt.io/qt-6/qmediaplayer.html#details
May be this is as variant, but i think it not good:
QTemporaryFile tfile;
if (!tfile.open())
qDebug() << "TemporaryFile not opened";
else
{
qDebug() << "TemporaryFile writed:" << tfile.write(ba);
if(tfile.size() != ba.size())
qDebug() << "TemporaryFile not complited";
else
player->setSource(QUrl::fromLocalFile(tfile.fileName()));
}

QFile. Device not open

I have problem with QFile.
QFile file1("file1.dat");
QFile file2("file2.dat");
if(file2.exists())
{
}
if(!file1.open(QIODevice::ReadOnly))
{
qDebug() << "Ошибка открытия для чтения";
}
if(!file2.open(QIODevice::WriteOnly))
{
qDebug() << "Ошибка открытия для записи";
}
QByteArray block = file1.readAll();
file2.write(block);
file1.close();
file2.close();
ERROR:
QIODevice::read (QFile, "file1.dat"): device not open
try to open file1.dat in read-write mode:
if(!file1.open(QIODevice::ReadWrite))
{
qDebug() << "Ошибка открытия";
}
Because if you open it just for reading, it cannot be created if it doesnt exist, or create it manually at first.
and in case that file is not opened you are not doing anything, so just for being sure check if both files were opened at first:
if(file1.isOpen() && file2.isOpen()){
QByteArray block = file1.readAll();
file2.write(block);
file1.close();
file2.close();
}

How to retrieve HTTPS json data using QNetworkRequest

I have some code that successfully retrieves json content from an HTTP source:
void MainWindow::Test()
{
QNetworkRequest request;
QString filename = "http://ip.jsontest.com";
//QString filename = "https://api.github.com/users/mralexgray/repos";
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
request.setUrl(QUrl(filename));
QJsonObject json;
QNetworkAccessManager nam;
QNetworkReply *reply = nam.post(request, QJsonDocument(json).toJson());
while(!reply->isFinished())
{
qApp->processEvents();
}
if (reply->error() == QNetworkReply::NoError)
{
QByteArray response_data = reply->readAll();
QJsonDocument document = QJsonDocument::fromJson(response_data);
QJsonObject object = document.object();
qDebug() << "Json File Loaded : " << filename;
qDebug() << "IP: " << object["ip"].toString();
}
else // something went wrong
{
qDebug() << "Json File Failed to Load : " << filename;
qDebug() << "Error : " << reply->errorString();
}
reply->deleteLater();
}
However, when I change the URL to the HTTPS one (the line commented out in the code) I get the error "server replied: Not Found"
The HTTPS url works fine in a browser.
In my search for answers I found many different suggestions but no actual working solution.
I believe the problem is down to QT not supporting SSL 'out of the box' due to licensing issues, but here's what I've attempted so far.
I installed "Win32OpenSSL-1_0_2h.exe"
I then copied "libeay32.dll" and "ssleay32.dll" into the QT debug folder.
I added the following to the project "pro" file:
LIBS += -LC:/OpenSSL-Win32/lib -lubsec
INCLUDEPATH += C:/OpenSSL-Win32/include
I tried with and without the setSslConfiguration call.
But all this hasn't helped.
I tested SSL with the QT method:
if (QSslSocket::supportsSsl())
{
qDebug() << "Supports SSL";
}
else
{
qDebug() << "Does NOT Support SSL";
}
and that is saying "supported".
I am building the project in QT Creator, using MSVC2012 32bit settings.

Saving downloaded file in Qt

I have my slot being called whenever someone clicks the link and I know the file is there because I can retrieve the file name and the amount of bytes the file is just not sure how to s ave it after I call the QFileDialog::getSaveFileName? I know that gives me the name of the file if the user decides to change it but how do I get the location they decide to save it in and then write it to that location.
NB: The file they will download is a word doc if that makes any difference?
void MainWindow::unsupportedContent(QNetworkReply *reply) {
qDebug() << "Left click - download!";
qDebug() << "Bytes to download: " << reply->bytesAvailable();
QString str = reply->rawHeader("Content-Disposition");
QString end = str.mid(21);
end.chop(1);
qDebug() << "string: " << end;
qDebug() << "File name: " << reply->rawHeader("Content-Disposition");
qDebug() << "File type: " << reply->rawHeader("Content-Type");
QString defaultFileName = QFileInfo(end).fileName();
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName);
if (fileName.isEmpty()) return;
QFile *file = new QFile(fileName);
file->open(fileName);
file->write(reply->read(reply->bytesAvailable()));
file->close();
}
today i explained it on another post so i will link that post here : Post
i can tell you that you have only to implement a slot that write into the file you created. and call it when readyRead() signal is emitted.