Problems with file download - c++

I have this QT script using webkit. I can download files no problem but I can't get the progress bar moving in the file dialog. I think the network reply has already been sent before I call the progress dialog as there is a delay from clicking the download link and then then qDebug() << "Left click - download!"; being echo's out into console. How can I intercept the netwrok reply before it has finished and the unsupportedContent() method is called?
EDIT:
I could strip it out and use reply = manager.get(QNetworkRequest(url)); but I don't actually know the URL it could be any link the user clicks, there is no predefined URL?
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() << "File name: " << end;
qDebug() << "File type: " << reply->rawHeader("Content-Type");
qDebug() << "File size (bytes): " << reply->bytesAvailable();
QString defaultFileName = QFileInfo(end).fileName();
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName);
if (fileName.isEmpty()) return;
file = new QFile(fileName);
if(!file->open(QIODevice::WriteOnly))
{
QMessageBox::information(this, "Downloader",
tr("Unable to save the file %1: %2.")
.arg(fileName).arg(file->errorString()));
delete file;
file = NULL;
return;
}
downloadRequestAborted = false;
connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
connect(reply, SIGNAL(readyRead()), this, SLOT(downloadReadyRead()));
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));
connect(progressDialog, SIGNAL(canceled()), this, SLOT(cancelDownload()));
progressDialog->setLabelText(tr("Downloading %1...").arg(fileName));
//downloadButton->setEnabled(false);
progressDialog->exec();
//QFile file(fileName);
//file.open( QIODevice::WriteOnly );
//file.write(reply->read(reply->bytesAvailable()));
//file.close();
}
void MainWindow::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
qDebug() << bytesReceived << bytesTotal;
if(downloadRequestAborted)
return;
progressDialog->setMaximum(bytesTotal);
progressDialog->setValue(bytesReceived);
}
void MainWindow::downloadReadyRead()
{
if(file)
file->write(reply->read(reply->bytesAvailable()));
}
void MainWindow::downloadFinished()
{
qDebug() << "Download finished!";
if(downloadRequestAborted)
{
if(file)
{
file->close();
file->remove();
delete file;
file = NULL;
}
reply->deleteLater();
progressDialog->hide();
//downloadButton->setEnabled(true);
return;
}
downloadReadyRead();
progressDialog->hide();
//downloadButton->setEnabled(true);
file->flush();
file->close();
if(reply->error())
{
//Download failed
QMessageBox::information(this, "Download failed", tr("Failed: %1").arg(reply->errorString()));
}
reply->deleteLater();
reply = NULL;
delete file;
file = NULL;
}
void MainWindow::cancelDownload()
{
downloadRequestAborted = true;
reply->abort();
progressDialog->hide();
//downloadButton->setEnabled(true);
}

The above method was working the whole time the problem was the bytes it was receiving was so small you could not tell it had downloaded at all, once I attempted to download a larger file the bytes being downloaded were adequately displayed :)
Here is the method I ended up with that could receive a request and save it disk.
void MainWindow::unsupportedContent(QNetworkReply *reply) {
QString str = reply->rawHeader("Content-Disposition");
QString end = str.mid(21);
end.chop(1);
QString defaultFileName = QFileInfo(end).fileName();
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), defaultFileName);
if (fileName.isEmpty()) return;
file = new QFile(fileName);
if(!file->open(QIODevice::WriteOnly))
{
QMessageBox::information(this, "Downloader",
tr("Unable to save the file %1: %2.")
.arg(fileName).arg(file->errorString()));
delete file;
file = NULL;
return;
}
downloadRequestAborted = false;
if(!reply->isFinished()){
connect(reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64)));
connect(progressDialog, SIGNAL(canceled()), SLOT(cancelDownload()));
progressDialog->setLabelText(tr("Downloading %1...").arg(fileName));
progressDialog->exec();
//return;
}
if(downloadRequestAborted)
{
if(file)
{
file->close();
file->remove();
delete file;
file = NULL;
}
reply->abort();
reply->deleteLater();
progressDialog->hide();
return;
}
file->write(reply->read(reply->bytesAvailable()));
file->flush();
file->close();
file = NULL;
if(file == NULL){
isDownload = true;
fileURL = fileName;
systray->showMessage("CytoViewer v1.0", "Download finished - Click to open", QSystemTrayIcon::NoIcon, 10000);
}
}

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?

Trouble when using QNetworkReply in QT

I'm new in C++ also with QT, my current QT version is 5.5.1.
I have an issue when using connect with QNetworkReply and QNetworkAccessManager. When I try to assign a variable from another file to data fetched from API, it always calls the function first before connecting to the API so that the variable is always empty. Can you guys help me?
void NioshFunc::fetchDataFromAPI(){
QUrl url;
QUrlQuery querystr;
querystr.addQueryItem("$format","json");
url.setScheme("https");
url.setHost("example.com");
url.setPath("/aaaa/dataset");
url.setQuery(querystr);
QNetworkRequest request;
request.setUrl(url);
request.setRawHeader("KeyId", "xxx-xxx-xxx");
reply = manager->get(request);
connect(reply, &QNetworkReply::readyRead, this, &NioshFunc::readyRead);
connect(reply, &QNetworkReply::finished, this, &NioshFunc::finished);
}
void NioshFunc::readyRead(){
dataBuffer.append(reply->readAll());
}
void NioshFunc::finished(){
if(reply->error())
{
qDebug() << "ERROR!";
qDebug() << reply->errorString();
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
}else{
qDebug() << "SUCCESS";
qDebug() << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
nioshWPDataFromAPI = QJsonDocument::fromJson(dataBuffer);
qDebug() << nioshWPDataFromAPI;
dataBuffer.clear();
}
reply->deleteLater();
}
bool NioshFunc::parse(){
Globals *g = Globals::getHandle();
if (nioshWPDataFromAPI.isEmpty() || nioshWPDataFromAPI.isNull()) return false;
QVariantMap root = nioshWPDataFromAPI.toVariant().toMap();
if (root.isEmpty()) return false;
QVariantMap d = root["d"].toMap();
if (d.isEmpty()) return false;
QVariantList results = d["results"].toList();
if (results.isEmpty()) return false;
foreach (QVariant varResult, results)
{
QVariantMap result = varResult.toMap();
if (result.isEmpty()) return false;
g->struct_tableColumn.input.duration = result["Duration"].toString();
g->struct_tableColumn.input.id = result["Id"].toString();
if (id.isEmpty() || id.isNull()) return false;
g->struct_tableColumn.input.load_cumul_Limit = result["LoadCumulLimit"].toString();
g->struct_tableColumn.input.Notes = result["Notes"].toString();
qDebug() << g->struct_tableColumn.input.id << g->struct_tableColumn.input.duration << g->struct_tableColumn.input.load_cumul_Limit << g->struct_tableColumn.input.Notes; // "it is empty for all variable from globals file.
}
return true;
}
When I can function fetchDataFromAPI() and call function parse() in another file named globals, that is always called 2 these functions first before connecting to API, so that the variable named "nioshWPDataFromAPI" is always empty. But when I read nioshWPDataFromAPI in function finished(), it still has data, but only in function finished(). Could you guys help me? Sorry if my question is not too clear for you guys

QT C++ how to append text on last lıne and keep old lınes?

Instead of overwriting the new text, I want to preserve the existing content and add it to the new line.
I need make a simple log for login. But When I try save to txt file Is overwriting on file. But when I try it, It just overwriting on file. How can I add to new line when new log is coming?
Here is my login function
void loginpage::on_lgnBtn_clicked() {
login = ui -> lgnUname -> text();
password = ui -> lgnPass -> text();
QString passwordhash = passwordHash(password.toUtf8());
qDebug() << passwordhash;
if (login == "admin" && passwordhash == getTxtPassword()) {
loginLog();
hide();
mw = new MainWindow(this);
mw -> show();
} else {
QMessageBox::warning(nullptr, "Hata", "Kullanıcı adınız veya şifreniz hatalı!");
loginLog();
}
}
Here is my function for write to file.
void loginpage::loginLog() {
QString TEXT_DIR = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/test/log.txt";
QFile file(TEXT_DIR);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream stream( & file);
QString UnameLog = login;
QString PassLog = password;
stream << "Giriş yapıldı";
stream << "\n";
stream << UnameLog;
stream << "\n";
qDebug()<< UnameLog;
stream << PassLog;
file.close();
} else {
qDebug() << "hata log yazılamadı";
}
}
I think there is something missing or wrong, but I couldn't figure it out. Can you help me please?
Did you check the documentation? The flag you want is QIODevice::Append.

Rename file if button is pressed in TreeView Qt

I have a treeview that displays a folder with text files. There is a 'open' button. That will open the file. But when this button is pressed it should rename the file to: read filename.txt. So if there is a file for example that is name nameslist.txt and the button is pressed it should rename it to read nameslist.txt or something similar. I thought of something like this:
void berichtenhistorie::on_Openbutton_released()
{
QModelIndex index = ui->treeView->currentIndex();
QString name = index.fileName();
QString path = index.filePath();
QFile file(path);
file.open(QIODevice::WriteOnly | QIODevice::Text);
file.rename("read " + name);
file.close();
}
But this isnt working. I get the following error's:
error: C2352: 'QDirModel::fileName' : illegal call of non-static member function
But I dont know how to use fileName() and filePath() correctly.
Thanks for help!
I think that here is what you looking for:
void berichtenhistorie::on_Openbutton_released()
{
QModelIndex index = ui->treeView->currentIndex();
QFileSystemModel *model = (QFileSystemModel*)ui->treeView->model();
QString path = model->filePath(index);
QString name = model->fileName(index);
QString dir = path;
dir.remove(dir.size() - name.size(), name.size());
QFile file(path);
if(file.open(QIODevice::WriteOnly | QIODevice::Text))
{
//Interact with the file
file.close();
if(file.rename(QString("%1read %2").arg(dir, name)))
qDebug() << "Renamed";
}
}
Here's a break out of each step you'll need to do to work with QFile.
QFile file("test.txt");
if(file.exists())
{
qDebug() << "found file";
if(file.open(QIODevice::ReadWrite))
{
qDebug() << "opened";
if(file.rename("text1.txt"))
{
qDebug() << "renamed";
}
else
{
qDebug() << "failed to rename";
}
file.close();
}
}
else
{
qDebug() << "file does not exist";
}
In the end, you'll only really need your debugger to step through instead of printing out everything known to your application. E.g.
QFile file("test.txt");
if(file.exists() && file.open(QIODevice::ReadWrite))
{
if(file.rename("text1.txt"))
{
qDebug() << "renamed";
}
file.close();
}

QHttpMultiPart send post request results in "1"

I am currently using QHttpMultiPart in a Qt Project, but it seems to have some problems on my end ?
I have followed the example and came up with the following code:
#include "uploader.h"
#include <QFileInfo>
#include <QMimeDatabase>
#include <QHttpMultiPart>
#include <QNetworkReply>
#include <QDebug>
/**
* #brief Uploader::Uploader
* #param parent
*/
Uploader::Uploader(QObject *parent) :
QObject(parent)
{
uploadInProgress = false;
}
/**
* #brief Uploader::upload
* #param absoluteFilePath
*/
void Uploader::upload(QString absoluteFilePath)
{
qDebug() << "Upload Starting";
QFileInfo fileInfo(absoluteFilePath);
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
//action part
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"cmd\""));
textPart.setBody(QString("wFile").toLatin1());
//File Path
QHttpPart filePathPart;
filePathPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file_path\""));
filePathPart.setBody(absoluteFilePath.toLatin1());
//filepart
QHttpPart filePart;
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(absoluteFilePath);
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(mime.name()));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"preview_file\"; filename=\""+ fileInfo.baseName() + "\""));
QFile *file = new QFile(absoluteFilePath);
if ( !file->exists() )
{
qDebug() << "File Does not exist";
}
file->open(QIODevice::ReadOnly);
filePart.setBodyDevice(file);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(textPart);
multiPart->append(filePathPart);
multiPart->append(filePart);
QUrl url("http://project.dbz.dev/index.php?controller=wapi&action=handle");
QNetworkRequest request(url);
pManager = new QNetworkAccessManager();
pReply = pManager->post(request, multiPart);
multiPart->setParent(pReply);
connect(pReply, SIGNAL(uploadProgress(qint64,qint64)),this,SLOT(uploadProgress(qint64,qint64)));
connect(pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
connect(pReply, SIGNAL(finished()),this, SLOT(uploadFinished()));
// here connect signals etc.
uploadInProgress = true;
}
/**
* #brief Uploader::uploadFinished
*/
void Uploader::uploadFinished()
{
QString data = (QString) pReply->readAll();
qDebug() << data;
qDebug() << "Upload finished";
uploadInProgress = false;
if ( pReply->error() > 0 )
{
qDebug() << "Error occured: " << pReply->error() << " : " << pReply->errorString();
}
else
{
qDebug() << "Upload success";
}
delete pReply;
}
void Uploader::uploadProgress(qint64 a, qint64 b)
{
qDebug() << " SOME PROGRESS!";
qDebug() << a << "/" << b;
}
void Uploader::onError(QNetworkReply::NetworkError err)
{
qDebug() << " SOME ERROR!";
qDebug() << err;
}
Sadly, none of the SLOTS are triggered from the SIGNALS. Neither can I see a package send with wireshark on my local ethernet adapter.
However, my Apache does get a request:
192.168.178.21 - - [21/Sep/2013:05:10:41 +0200] "POST /index.php?controller=wapi&action=handle HTTP/1.1" 200 166 "-" "Mozilla/5.0"
And in my PHP Application I have the following outcome:
Application_Controller_WapiController::handleAction: Command: wFile
Application_Controller_WapiController::wFile: POST Request: 1
This, basically means, it recognises the Parameter "cmd" and the value "wFile", opens the according PHP action which then does a print_r($_POST) which shows me nothing more than a simple 1.
I have no idea what to do. I have looked everywhere on the internet and cannot seem to figure it out. I followed all examples and descriptions on the official documentary and found a couple of threads here on SO. There seemed to be a bug with the QHttpMultiPart class, although it was fixed with the major 5.0.0 update.
tl;dr:
connect(pReply, SIGNAL(uploadProgress(qint64,qint64)),this,SLOT(uploadProgress(qint64,qint64)));
connect(pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
connect(pReply, SIGNAL(finished()),this, SLOT(uploadFinished()));
These signals are not being triggered, PHP print_r shows me a 1 and I cannot track the POST request on my machine.
It would be nice if somebody could tell me why the SIGNALS are not emitted and more importantly how I can see a final version of my POST request in my C++ application before it is sent.
Thank you very much! I appreciate any help!
I have solved the problem by adding:
pELoop = new QEventLoop();
pELoop->exec();
Which results into this:
#include "uploader.h"
#include <QFileInfo>
#include <QMimeDatabase>
#include <QHttpMultiPart>
#include <QNetworkReply>
#include <QDebug>
/**
* #brief Uploader::Uploader
* #param parent
*/
Uploader::Uploader(QObject *parent) :
QObject(parent)
{
uploadInProgress = false;
}
/**
* #brief Uploader::upload
* #param absoluteFilePath
*/
void Uploader::upload(QString absoluteFilePath)
{
qDebug() << "Upload Starting";
QFileInfo fileInfo(absoluteFilePath);
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
//action part
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"cmd\""));
textPart.setBody(QString("wFile").toLatin1());
//File Path
QHttpPart filePathPart;
filePathPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file_path\""));
filePathPart.setBody(absoluteFilePath.toLatin1());
//filepart
QHttpPart filePart;
QMimeDatabase db;
QMimeType mime = db.mimeTypeForFile(absoluteFilePath);
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(mime.name()));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"preview_file\"; filename=\""+ fileInfo.baseName() + "\""));
QFile *file = new QFile(absoluteFilePath);
if ( !file->exists() )
{
qDebug() << "File Does not exist";
}
file->open(QIODevice::ReadOnly);
filePart.setBodyDevice(file);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(textPart);
multiPart->append(filePathPart);
multiPart->append(filePart);
QUrl url("http://encryptor.dbz.dev/index.php?controller=wapi&action=handle");
QNetworkRequest request(url);
pManager = new QNetworkAccessManager();
pReply = pManager->post(request, multiPart);
multiPart->setParent(pReply);
pELoop = new QEventLoop();
connect(pReply, SIGNAL(uploadProgress(qint64,qint64)),this,SLOT(uploadProgress(qint64,qint64)));
connect(pReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onError(QNetworkReply::NetworkError)));
connect(pReply, SIGNAL(finished()),this, SLOT(uploadFinished()));
pELoop->exec();
// here connect signals etc.
uploadInProgress = true;
}
/**
* #brief Uploader::uploadFinished
*/
void Uploader::uploadFinished()
{
QString data = (QString) pReply->readAll();
qDebug() << data;
qDebug() << "Upload finished";
uploadInProgress = false;
if ( pReply->error() > 0 )
{
qDebug() << "Error occured: " << pReply->error() << " : " << pReply->errorString();
}
else
{
qDebug() << "Upload success";
}
pReply->deleteLater();
pELoop->exit();
}
void Uploader::uploadProgress(qint64 a, qint64 b)
{
qDebug() << " SOME PROGRESS!";
qDebug() << a << "/" << b;
}
void Uploader::onError(QNetworkReply::NetworkError err)
{
qDebug() << " SOME ERROR!";
qDebug() << err;
}
The request is executed as expected, and the signals are working as well.
I get the output of:
Upload Starting
SOME PROGRESS!
16384 / 483753
SOME PROGRESS!
483753 / 483753
SOME PROGRESS!
0 / 0
"Array
(
[controller] => wapi
[action] => handle
[cmd] => wFile
[file_path] => D:/Downloads/putty.exe
)
{"cmd":"","status":"","message":"","params":[]}"
Upload finished
Upload success
I leave this hear in case somebody is looking for a working example.