QT下载大文件错误 [英] QT downloading large file error

查看:658
本文介绍了QT下载大文件错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我尝试下载文件达到50mb的例子,没问题,但是大文件给出以下错误

  void MainWindow :: downloadFile(){
QNetworkRequest请求;
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 *)),这个,SLOT(downloadFinished(QNetworkReply *)));
}


void MainWindow :: downloadFinished(QNetworkReply * data){
QFile localFile(fileName);
if(!localFile.open(QIODevice :: WriteOnly))
return;
localFile.write(data-> readAll());
localFile.close();
}


解决方案

在你的代码中,你是将整个文件保存在内存中,直到下载过程完成(即

  #include< QtWidgets> 
#include< QtNetwork>

//一次下载一个文件,使用提供的QNetworkAccessManager对象
class FileDownloader:public QObject {
Q_OBJECT
public:
explicit FileDownloader QNetworkAccessManager * nam,QObject * parent = nullptr)
:QObject(父),nam(nam)
{

}
〜FileDownloader(){
//析构函数取消正在进行的下载(如果有)
if(networkReply){
a_abortDownload();
}
}

//调用此函数开始从url下载文件到fileName
void startDownload(QUrl url,QString fileName){
if(networkReply)return;
destinationFile.setFileName(fileName);
if(!destinationFile.open(QIODevice :: WriteOnly))return;
emit goingBusy();
QNetworkRequest请求(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);
}

//调用此函数来中止正在进行的下载(如果有)
void abortDownload(){
if(!networkReply)return;
a_abortDownload();
emit backReady();
}

//连接以下信号以获取有关正在进行的下载的信息
Q_SIGNAL void downloadProgress(qint64 bytesReceived,qint64 bytesTotal);
Q_SIGNAL void downloadSuccessful();
Q_SIGNAL void downloadError(QString errorString);
//接下来的两个信号用于指示文件下载器的busy和
// ready状态之间的转换,它们可用于更新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){
//失败下载
a_abortDownload );
emit downloadError(networkReply-> errorString());
} else {
//成功下载
QByteArray data = networkReply-> readAll();
destinationFile.write(data);
destinationFile.close();
networkReply-> deleteLater();
emit downloadSuccessful();
}
emit backReady();
}
//私有函数,当下载中止时清理东西
//(由于错误或用户交互)
void a_abortDownload(){
networkReply->中止();
networkReply-> deleteLater();
destinationFile.close();
destinationFile.remove();
}

QNetworkAccessManager * nam;
QUrl downloadUrl;
QFile destinationFile;
QPointer< QNetworkReply> networkReply;
};

//使用上述类的一个示例GUI应用程序
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);
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();
//使用来自FileDownloader信号的goingBusy()和backReady()来更新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);
返回;
}
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(成功下载);
}
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(下载...);
}
Q_SLOT void showReady(){
buttonDownload.setEnabled(true);
lineEditUrl.setEnabled(true);
buttonAbort.setEnabled(false);
progressBar.setRange(0,1);
progressBar.setValue(0);
}

QGridLayout布局{this};
QLineEdit lineEditUrl;
QPushButton buttonDownload {开始下载};
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();
}

#includemain.moc


When I try to downloading file up to 50mb example, no problem, but with a big files give the following error

 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();
    }

解决方案

In your code, You are holding the whole file in memory until the download process finishes (that is when QNetworkAccessManager::finished() signal gets emitted). Of course, this is not the best way to deal with large files.

Remember that QNetworkReply is a QIODevice. This means that you should use the readyRead() signal to save data chunks to the disk as soon as they are received from the network, in order to avoid holding the whole file in memory until the download is finished.

Here is a minimal complete example:

#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();
}

Update:

I have wrapped the whole thing in a class FileDownloader, which can be used to download a file using QNetworkAccessManager, Here is an example of using this class:

#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"

这篇关于QT下载大文件错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆