aboutsummaryrefslogtreecommitdiff
path: root/jobs
diff options
context:
space:
mode:
Diffstat (limited to 'jobs')
-rw-r--r--jobs/downloadfilejob.cpp113
-rw-r--r--jobs/downloadfilejob.h27
2 files changed, 140 insertions, 0 deletions
diff --git a/jobs/downloadfilejob.cpp b/jobs/downloadfilejob.cpp
new file mode 100644
index 00000000..2530e259
--- /dev/null
+++ b/jobs/downloadfilejob.cpp
@@ -0,0 +1,113 @@
+#include "downloadfilejob.h"
+
+#include <QtNetwork/QNetworkReply>
+#include <QtCore/QFile>
+#include <QtCore/QTemporaryFile>
+
+using namespace QMatrixClient;
+
+class DownloadFileJob::Private
+{
+ public:
+ Private() : tempFile(new QTemporaryFile()) { }
+
+ explicit Private(const QString& localFilename)
+ : targetFile(new QFile(localFilename))
+ , tempFile(new QFile(targetFile->fileName() + ".qmcdownload"))
+ { }
+
+ QScopedPointer<QFile> targetFile;
+ QScopedPointer<QFile> tempFile;
+};
+
+DownloadFileJob::DownloadFileJob(const QString& serverName,
+ const QString& mediaId,
+ const QString& localFilename)
+ : GetContentJob(serverName, mediaId)
+ , d(localFilename.isEmpty() ? new Private : new Private(localFilename))
+{
+ setObjectName("DownloadFileJob");
+}
+
+QString DownloadFileJob::targetFileName() const
+{
+ return (d->targetFile ? d->targetFile : d->tempFile)->fileName();
+}
+
+void DownloadFileJob::beforeStart(const ConnectionData*)
+{
+ if (d->targetFile && !d->targetFile->open(QIODevice::WriteOnly))
+ {
+ qCWarning(JOBS) << "Couldn't open the file"
+ << d->targetFile->fileName() << "for writing";
+ setStatus(FileError, "Could not open the target file for writing");
+ return;
+ }
+ if (!d->tempFile->open(QIODevice::WriteOnly))
+ {
+ qCWarning(JOBS) << "Couldn't open the temporary file"
+ << d->tempFile->fileName() << "for writing";
+ setStatus(FileError, "Could not open the temporary download file");
+ }
+ qCDebug(JOBS) << "Downloading to" << d->tempFile->fileName();
+}
+
+void DownloadFileJob::afterStart(const ConnectionData*, QNetworkReply* reply)
+{
+ connect(reply, &QNetworkReply::metaDataChanged, this, [this,reply] {
+ auto sizeHeader = reply->header(QNetworkRequest::ContentLengthHeader);
+ if (sizeHeader.isValid())
+ {
+ auto targetSize = sizeHeader.value<qint64>();
+ if (targetSize != -1)
+ if (!d->tempFile->resize(targetSize))
+ {
+ qCWarning(JOBS) << "Failed to allocate" << targetSize
+ << "bytes for" << d->tempFile->fileName();
+ setStatus(FileError,
+ "Could not reserve disk space for download");
+ }
+ }
+ });
+ connect(reply, &QIODevice::readyRead, this, [this,reply] {
+ auto bytes = reply->read(reply->bytesAvailable());
+ if (bytes.isEmpty())
+ {
+ qCWarning(JOBS)
+ << "Unexpected empty chunk when downloading from"
+ << reply->url() << "to" << d->tempFile->fileName();
+ } else {
+ d->tempFile->write(bytes);
+ }
+ });
+}
+
+void DownloadFileJob::beforeAbandon(QNetworkReply*)
+{
+ if (d->targetFile)
+ d->targetFile->remove();
+ d->tempFile->remove();
+}
+
+BaseJob::Status DownloadFileJob::parseReply(QNetworkReply*)
+{
+ if (d->targetFile)
+ {
+ d->targetFile->close();
+ if (!d->targetFile->remove())
+ {
+ qCWarning(JOBS) << "Failed to remove the target file placeholder";
+ return { FileError, "Couldn't finalise the download" };
+ }
+ if (!d->tempFile->rename(d->targetFile->fileName()))
+ {
+ qCWarning(JOBS) << "Failed to rename" << d->tempFile->fileName()
+ << "to" << d->targetFile->fileName();
+ return { FileError, "Couldn't finalise the download" };
+ }
+ }
+ else
+ d->tempFile->close();
+ qCDebug(JOBS) << "Saved a file as" << targetFileName();
+ return Success;
+}
diff --git a/jobs/downloadfilejob.h b/jobs/downloadfilejob.h
new file mode 100644
index 00000000..d798506c
--- /dev/null
+++ b/jobs/downloadfilejob.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "generated/content-repo.h"
+
+namespace QMatrixClient
+{
+ class DownloadFileJob : public GetContentJob
+ {
+ public:
+ enum { FileError = BaseJob::UserDefinedError + 1 };
+
+ DownloadFileJob(const QString& serverName, const QString& mediaId,
+ const QString& localFilename = {});
+
+ QString targetFileName() const;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+
+ void beforeStart(const ConnectionData*) override;
+ void afterStart(const ConnectionData*,
+ QNetworkReply* reply) override;
+ void beforeAbandon(QNetworkReply*) override;
+ Status parseReply(QNetworkReply*) override;
+ };
+}