当我尝试下载最高50mb的文件时,没问题,但是有大文件会出现以下错误
void MainWindow::downloadFile() {
QNetworkRequest requests;
requests.setUrl(QUrl("https://urlToFile"));
QSslConfiguration configSsl = QSslConfiguration::defaultConfiguration();
configSsl.setProtocol(QSsl::AnyProtocol);
requests.setSslConfiguration(configSsl);
QNetworkAccessManager *manager5 = new QNetworkAccessManager(this);
QNetworkReply *reply5;
reply5 = manager5->get( requests );
connect(manager5, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*)));
}
void MainWindow::downloadFinished(QNetworkReply *data) {
QFile localFile("fileName");
if (!localFile.open(QIODevice::WriteOnly))
return;
localFile.write(data->readAll());
localFile.close();
}
答案 0 :(得分:5)
在您的代码中,您将整个文件保留在内存中,直到下载过程完成(即发出QNetworkAccessManager::finished()
信号时)。当然,这不是处理大文件的最佳方式。
请注意QNetworkReply
是QIODevice
。这意味着,一旦从网络接收到数据块,就应该使用readyRead()
信号将数据块保存到磁盘,以避免在下载完成之前将整个文件保留在内存中。
这是一个最小的完整示例:
#include <QtNetwork>
int main(int argc, char* argv[]){
QCoreApplication a(argc, argv);
QNetworkAccessManager nam;
QFile file("downloadedFile.xxx");
if(!file.open(QIODevice::ReadWrite)) return 1;
QNetworkRequest request(QUrl("http://download_url/..."));
QNetworkReply* reply = nam.get(request);
QObject::connect(reply, &QNetworkReply::readyRead, [&]{
//this will be called every time a chunk of data is received
QByteArray data= reply->readAll();
qDebug() << "received data of size: " << data.size();
file.write(data);
});
//use the finished signal from the reply object to close the file
//and delete the reply object
QObject::connect(reply, &QNetworkReply::finished, [&]{
qDebug() << "finished downloading";
QByteArray data= reply->readAll();
file.write(data);
file.close();
reply->deleteLater();
a.quit();
});
return a.exec();
}
我已将整个内容包装在类FileDownloader
中,可用于使用QNetworkAccessManager
下载文件,以下是使用此类的示例:
#include <QtWidgets>
#include <QtNetwork>
//downloads one file at a time, using a supplied QNetworkAccessManager object
class FileDownloader : public QObject{
Q_OBJECT
public:
explicit FileDownloader(QNetworkAccessManager* nam, QObject* parent= nullptr)
:QObject(parent),nam(nam)
{
}
~FileDownloader(){
//destructor cancels the ongoing dowload (if any)
if(networkReply){
a_abortDownload();
}
}
//call this function to start downloading a file from url to fileName
void startDownload(QUrl url, QString fileName){
if(networkReply) return;
destinationFile.setFileName(fileName);
if(!destinationFile.open(QIODevice::WriteOnly)) return;
emit goingBusy();
QNetworkRequest request(url);
networkReply= nam->get(request);
connect(networkReply, &QIODevice::readyRead, this, &FileDownloader::readData);
connect(networkReply, &QNetworkReply::downloadProgress,
this, &FileDownloader::downloadProgress);
connect(networkReply, &QNetworkReply::finished,
this, &FileDownloader::finishDownload);
}
//call this function to abort the ongoing download (if any)
void abortDownload(){
if(!networkReply) return;
a_abortDownload();
emit backReady();
}
//connect to the following signals to get information about the ongoing download
Q_SIGNAL void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
Q_SIGNAL void downloadSuccessful();
Q_SIGNAL void downloadError(QString errorString);
//the next two signals are used to indicate transitions between busy and
//ready states of the file downloader, they can be used to update the GUI
Q_SIGNAL void goingBusy();
Q_SIGNAL void backReady();
private:
Q_SLOT void readData(){
QByteArray data= networkReply->readAll();
destinationFile.write(data);
}
Q_SLOT void finishDownload(){
if(networkReply->error() != QNetworkReply::NoError){
//failed download
a_abortDownload();
emit downloadError(networkReply->errorString());
} else {
//successful download
QByteArray data= networkReply->readAll();
destinationFile.write(data);
destinationFile.close();
networkReply->deleteLater();
emit downloadSuccessful();
}
emit backReady();
}
//private function, cleans things up when the download is aborted
//(due to an error or user interaction)
void a_abortDownload(){
networkReply->abort();
networkReply->deleteLater();
destinationFile.close();
destinationFile.remove();
}
QNetworkAccessManager* nam;
QUrl downloadUrl;
QFile destinationFile;
QPointer<QNetworkReply> networkReply;
};
//A sample GUI application that uses the above class
class Widget : public QWidget{
Q_OBJECT
public:
explicit Widget(QWidget* parent= nullptr):QWidget(parent){
layout.addWidget(&lineEditUrl, 0, 0);
layout.addWidget(&buttonDownload, 0, 1);
layout.addWidget(&progressBar, 1, 0);
layout.addWidget(&buttonAbort, 1, 1);
layout.addWidget(&labelStatus, 2, 0, 1, 2);
lineEditUrl.setPlaceholderText("URL to download");
connect(&fileDownloader, &FileDownloader::downloadSuccessful,
this, &Widget::downloadFinished);
connect(&fileDownloader, &FileDownloader::downloadError,
this, &Widget::error);
connect(&fileDownloader, &FileDownloader::downloadProgress,
this, &Widget::updateProgress);
connect(&buttonDownload, &QPushButton::clicked,
this, &Widget::startDownload);
connect(&buttonAbort, &QPushButton::clicked,
this, &Widget::abortDownload);
showReady();
//use goingBusy() and backReady() from FileDownloader signals to update the GUI
connect(&fileDownloader, &FileDownloader::goingBusy, this, &Widget::showBusy);
connect(&fileDownloader, &FileDownloader::backReady, this, &Widget::showReady);
}
~Widget() = default;
Q_SLOT void startDownload(){
if(lineEditUrl.text().isEmpty()){
QMessageBox::critical(this, "Error", "Enter file Url", QMessageBox::Ok);
return;
}
QString fileName =
QFileDialog::getSaveFileName(this, "Destination File");
if(fileName.isEmpty()) return;
QUrl url= lineEditUrl.text();
fileDownloader.startDownload(url, fileName);
}
Q_SLOT void abortDownload(){
fileDownloader.abortDownload();
}
Q_SLOT void downloadFinished(){
labelStatus.setText("Download finished successfully");
}
Q_SLOT void error(QString errorString){
labelStatus.setText(errorString);
}
Q_SLOT void updateProgress(qint64 bytesReceived, qint64 bytesTotal){
progressBar.setRange(0, bytesTotal);
progressBar.setValue(bytesReceived);
}
private:
Q_SLOT void showBusy(){
buttonDownload.setEnabled(false);
lineEditUrl.setEnabled(false);
buttonAbort.setEnabled(true);
labelStatus.setText("Downloading. . .");
}
Q_SLOT void showReady(){
buttonDownload.setEnabled(true);
lineEditUrl.setEnabled(true);
buttonAbort.setEnabled(false);
progressBar.setRange(0,1);
progressBar.setValue(0);
}
QGridLayout layout{this};
QLineEdit lineEditUrl;
QPushButton buttonDownload{"Start Download"};
QProgressBar progressBar;
QPushButton buttonAbort{"Abort Download"};
QLabel labelStatus{"Idle"};
QNetworkAccessManager nam;
FileDownloader fileDownloader{&nam};
};
int main(int argc, char* argv[]){
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"