Save partial downloads on disk, not on RAM

This commit is contained in:
Alex Spataru 2016-11-13 17:56:10 -06:00
parent 62573c21bc
commit 1cbf20f156
4 changed files with 81 additions and 41 deletions

View File

@ -39,6 +39,9 @@
#include "Downloader.h" #include "Downloader.h"
static const QString PARTIAL_DOWN (".part");
static const QDir DOWNLOAD_DIR (QDir::homePath() + "/Downloads/");
Downloader::Downloader (QWidget* parent) : QWidget (parent) { Downloader::Downloader (QWidget* parent) : QWidget (parent) {
m_ui = new Ui::Downloader; m_ui = new Ui::Downloader;
m_ui->setupUi (this); m_ui->setupUi (this);
@ -47,7 +50,8 @@ Downloader::Downloader (QWidget* parent) : QWidget (parent) {
m_manager = new QNetworkAccessManager(); m_manager = new QNetworkAccessManager();
/* Initialize internal values */ /* Initialize internal values */
m_filePath = ""; m_url = "";
m_fileName = "";
m_startTime = 0; m_startTime = 0;
m_useCustomProcedures = false; m_useCustomProcedures = false;
@ -82,6 +86,17 @@ bool Downloader::useCustomInstallProcedures() const {
return m_useCustomProcedures; return m_useCustomProcedures;
} }
/**
* Changes the URL, which is used to indentify the downloader dialog
* with an \c Updater instance
*
* \note the \a url parameter is not the download URL, it is the URL of
* the AppCast file
*/
void Downloader::setUrlId (const QString& url) {
m_url = url;
}
/** /**
* Begins downloading the file at the given \a url * Begins downloading the file at the given \a url
*/ */
@ -96,25 +111,42 @@ void Downloader::startDownload (const QUrl& url) {
m_startTime = QDateTime::currentDateTime().toTime_t(); m_startTime = QDateTime::currentDateTime().toTime_t();
m_reply = m_manager->get (QNetworkRequest (url)); m_reply = m_manager->get (QNetworkRequest (url));
/* Ensure that downloads directory exists */
if (!DOWNLOAD_DIR.exists())
DOWNLOAD_DIR.mkpath (".");
/* Remove old downloads */
QFile::remove (DOWNLOAD_DIR.filePath (m_fileName));
QFile::remove (DOWNLOAD_DIR.filePath (m_fileName + PARTIAL_DOWN));
/* Update UI when download progress changes or download finishes */ /* Update UI when download progress changes or download finishes */
connect (m_reply, SIGNAL (downloadProgress (qint64, qint64)), connect (m_reply, SIGNAL (downloadProgress (qint64, qint64)),
this, SLOT (updateProgress (qint64, qint64))); this, SLOT (updateProgress (qint64, qint64)));
connect (m_reply, SIGNAL (redirected (QUrl)), connect (m_reply, SIGNAL (redirected (QUrl)),
this, SLOT (startDownload (QUrl))); this, SLOT (startDownload (QUrl)));
connect (m_reply, SIGNAL (finished()),
this, SLOT (onDownloadFinished()));
showNormal(); showNormal();
} }
/**
* Changes the name of the downloaded file
*/
void Downloader::setFileName (const QString& file) {
m_fileName = file;
if (m_fileName.isEmpty())
m_fileName = "QSU_Update.bin";
}
/** /**
* Opens the downloaded file. * Opens the downloaded file.
* \note If the downloaded file is not found, then the function will alert the * \note If the downloaded file is not found, then the function will alert the
* user about the error. * user about the error.
*/ */
void Downloader::openDownload() { void Downloader::openDownload() {
if (!m_filePath.isEmpty()) if (!m_fileName.isEmpty())
QDesktopServices::openUrl (QUrl::fromLocalFile (m_filePath)); QDesktopServices::openUrl (QUrl::fromLocalFile (DOWNLOAD_DIR.filePath (
m_fileName)));
else { else {
QMessageBox::critical (this, QMessageBox::critical (this,
@ -136,6 +168,13 @@ void Downloader::installUpdate() {
if (useCustomInstallProcedures()) if (useCustomInstallProcedures())
return; return;
/* Update labels */
m_ui->stopButton->setText (tr ("Close"));
m_ui->downloadLabel->setText (tr ("Download complete!"));
m_ui->timeLabel->setText (tr ("The installer will open separately")
+ "...");
/* Ask the user to install the download */
QMessageBox box; QMessageBox box;
box.setIcon (QMessageBox::Question); box.setIcon (QMessageBox::Question);
box.setDefaultButton (QMessageBox::Ok); box.setDefaultButton (QMessageBox::Ok);
@ -146,11 +185,13 @@ void Downloader::installUpdate() {
"quit the application.") "quit the application.")
+ "</h3>"); + "</h3>");
/* User wants to install the download */
if (box.exec() == QMessageBox::Ok) { if (box.exec() == QMessageBox::Ok) {
if (!useCustomInstallProcedures()) if (!useCustomInstallProcedures())
openDownload(); openDownload();
} }
/* Wait */
else { else {
m_ui->openButton->setEnabled (true); m_ui->openButton->setEnabled (true);
m_ui->openButton->setVisible (true); m_ui->openButton->setVisible (true);
@ -182,24 +223,9 @@ void Downloader::cancelDownload() {
} }
/** /**
* Writes the downloaded data to a temp. directory and updates the UI controls. * Writes the downloaded data to the disk
* \note If the function detects that the downloaded data is an HTML file
* (e.g. a redirection notice from the server), the function will add the
* *.html extension to the downloaded file. This ensures that the download
* will be resumed when the OS opens a web-browser with the redirection
* notice.
*/ */
void Downloader::onDownloadFinished() { void Downloader::saveFile (qint64 received, qint64 total) {
m_ui->stopButton->setText (tr ("Close"));
m_ui->downloadLabel->setText (tr ("Download complete!"));
m_ui->timeLabel->setText (tr ("The installer will open separately")
+ "...");
QByteArray data = m_reply->readAll();
if (!data.isEmpty()) {
QString name = m_reply->url().toString().split ("/").last();
/* Check if we need to redirect */ /* Check if we need to redirect */
QUrl url = m_reply->attribute ( QUrl url = m_reply->attribute (
QNetworkRequest::RedirectionTargetAttribute).toUrl(); QNetworkRequest::RedirectionTargetAttribute).toUrl();
@ -209,21 +235,28 @@ void Downloader::onDownloadFinished() {
} }
/* Save downloaded data to disk */ /* Save downloaded data to disk */
QFile file (QDir::tempPath() + "/" + name); QFile file (DOWNLOAD_DIR.filePath (m_fileName + PARTIAL_DOWN));
if (file.open (QIODevice::WriteOnly)) { if (file.open (QIODevice::WriteOnly | QIODevice::Append)) {
file.write (data); file.write (m_reply->readAll());
file.close(); file.close();
m_filePath = file.fileName();
emit downloadFinished (m_reply->url().toString(), m_filePath);
} }
/* Open downloaded update */ /* Open downloaded update */
if (received >= total && total > 0) {
/* Rename file */
QFile::rename (DOWNLOAD_DIR.filePath (m_fileName + PARTIAL_DOWN),
DOWNLOAD_DIR.filePath (m_fileName));
/* Notify application */
emit downloadFinished (m_url, DOWNLOAD_DIR.filePath (m_fileName));
/* Install the update */
m_reply->close(); m_reply->close();
installUpdate(); installUpdate();
} }
} }
/** /**
* Calculates the appropiate size units (bytes, KB or MB) for the received * Calculates the appropiate size units (bytes, KB or MB) for the received
* data and the total download size. Then, this function proceeds to update the * data and the total download size. Then, this function proceeds to update the
@ -268,6 +301,7 @@ void Downloader::updateProgress (qint64 received, qint64 total) {
calculateSizes (received, total); calculateSizes (received, total);
calculateTimeRemaining (received, total); calculateTimeRemaining (received, total);
saveFile (received, total);
} }
else { else {

View File

@ -56,14 +56,16 @@ class Downloader : public QWidget {
bool useCustomInstallProcedures() const; bool useCustomInstallProcedures() const;
public slots: public slots:
void setUrlId (const QString& url);
void startDownload (const QUrl& url); void startDownload (const QUrl& url);
void setFileName (const QString& file);
void setUseCustomInstallProcedures (const bool custom); void setUseCustomInstallProcedures (const bool custom);
private slots: private slots:
void openDownload(); void openDownload();
void installUpdate(); void installUpdate();
void cancelDownload(); void cancelDownload();
void onDownloadFinished(); void saveFile (qint64 received, qint64 total);
void calculateSizes (qint64 received, qint64 total); void calculateSizes (qint64 received, qint64 total);
void updateProgress (qint64 received, qint64 total); void updateProgress (qint64 received, qint64 total);
void calculateTimeRemaining (qint64 received, qint64 total); void calculateTimeRemaining (qint64 received, qint64 total);
@ -72,8 +74,9 @@ class Downloader : public QWidget {
qreal round (const qreal& input); qreal round (const qreal& input);
private: private:
QString m_url;
uint m_startTime; uint m_startTime;
QString m_filePath; QString m_fileName;
Ui::Downloader* m_ui; Ui::Downloader* m_ui;
QNetworkReply* m_reply; QNetworkReply* m_reply;
bool m_useCustomProcedures; bool m_useCustomProcedures;

View File

@ -362,8 +362,11 @@ void Updater::setUpdateAvailable (const bool available) {
if (!openUrl().isEmpty()) if (!openUrl().isEmpty())
QDesktopServices::openUrl (QUrl (openUrl())); QDesktopServices::openUrl (QUrl (openUrl()));
else if (downloaderEnabled()) else if (downloaderEnabled()) {
m_downloader->setUrlId (url());
m_downloader->setFileName (downloadUrl().split ("/").last());
m_downloader->startDownload (QUrl (downloadUrl())); m_downloader->startDownload (QUrl (downloadUrl()));
}
else else
QDesktopServices::openUrl (QUrl (downloadUrl())); QDesktopServices::openUrl (QUrl (downloadUrl()));