thread the AssetUpload so it doesn't take over main

This commit is contained in:
Stephen Birarda 2015-08-27 10:23:02 -07:00
parent c277584f2e
commit 513cae0d40
10 changed files with 201 additions and 37 deletions

View file

@ -80,11 +80,6 @@ void AssetServer::run() {
file.rename(_resourcesDirectory.absoluteFilePath(hash));
}
}
while (!_isFinished) {
// since we're a while loop we need to help Qt's event processing
QCoreApplication::processEvents();
}
}
void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {

View file

@ -18,9 +18,8 @@
OctreePacketProcessor::OctreePacketProcessor() {
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData,
PacketType::EntityErase, PacketType::OctreeStats },
packetReceiver.registerDirectListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
this, "handleOctreePacket");
}

View file

@ -12,10 +12,15 @@
#include "AssetUploadDialogFactory.h"
#include <AssetClient.h>
#include <AssetUpload.h>
#include <AssetUtils.h>
#include <QtCore/QDebug>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QDialogButtonBox>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QVBoxLayout>
AssetUploadDialogFactory& AssetUploadDialogFactory::getInstance() {
static AssetUploadDialogFactory staticInstance;
@ -27,29 +32,63 @@ AssetUploadDialogFactory::AssetUploadDialogFactory() {
}
void AssetUploadDialogFactory::showDialog() {
auto filename = QFileDialog::getOpenFileUrl(nullptr, "Select a file to upload");
auto filename = QFileDialog::getOpenFileUrl(_dialogParent, "Select a file to upload");
if (!filename.isEmpty()) {
qDebug() << "Selected filename for upload to asset-server: " << filename;
QFile file { filename.path() };
auto assetClient = DependencyManager::get<AssetClient>();
auto upload = assetClient->createUpload(filename.path());
if (file.open(QIODevice::ReadOnly)) {
if (upload) {
// connect to the finished signal so we know when the AssetUpload is done
QObject::connect(upload, &AssetUpload::finished, this, &AssetUploadDialogFactory::handleUploadFinished);
QFileInfo fileInfo { filename.path() };
auto extension = fileInfo.suffix();
auto data = file.readAll();
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->uploadAsset(data, extension, [this, extension](bool result, QString hash) mutable {
if (result) {
QMessageBox::information(_dialogParent, "Upload Successful", "URL: apt:/" + hash + "." + extension);
} else {
QMessageBox::warning(_dialogParent, "Upload Failed", "There was an error uploading the file.");
}
});
// start the upload now
upload->start();
}
}
}
void AssetUploadDialogFactory::handleUploadFinished(AssetUpload* upload, const QString& hash) {
if (true) {
// show message box for successful upload, with copiable text for ATP hash
QDialog* hashCopyDialog = new QDialog(_dialogParent);
// delete the dialog on close
hashCopyDialog->setAttribute(Qt::WA_DeleteOnClose);
// set the window title
hashCopyDialog->setWindowTitle(tr("Successful Asset Upload"));
// setup a layout for the contents of the dialog
QVBoxLayout* boxLayout = new QVBoxLayout;
// set the label text (this shows above the text box)
QLabel* lineEditLabel = new QLabel;
lineEditLabel->setText(QString("ATP URL for %1").arg(upload->getFilename()));
// setup the line edit to hold the copiable text
QLineEdit* lineEdit = new QLineEdit;
// set the ATP URL as the text value so it's copiable
lineEdit->insert(QString("%1://%2").arg(ATP_SCHEME).arg(hash));
// add the label and line edit to the dialog
boxLayout->addWidget(lineEditLabel);
boxLayout->addWidget(lineEdit);
// setup an OK button to close the dialog
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok);
connect(buttonBox, &QDialogButtonBox::accepted, hashCopyDialog, &QDialog::close);
boxLayout->addWidget(buttonBox);
// set the new layout on the dialog
hashCopyDialog->setLayout(boxLayout);
// show the new dialog
hashCopyDialog->show();
} else {
//
}
}

View file

@ -16,6 +16,8 @@
#include <QtCore/QObject>
class AssetUpload;
class AssetUploadDialogFactory : public QObject {
Q_OBJECT
public:
@ -28,6 +30,8 @@ public:
void setDialogParent(QWidget* dialogParent) { _dialogParent = dialogParent; }
public slots:
void showDialog();
private slots:
void handleUploadFinished(AssetUpload* upload, const QString& hash);
private:
QWidget* _dialogParent { nullptr };
};

View file

@ -14,6 +14,7 @@
#include <QThread>
#include "AssetRequest.h"
#include "AssetUpload.h"
#include "NodeList.h"
#include "PacketReceiver.h"
@ -27,7 +28,7 @@ AssetClient::AssetClient() {
packetReceiver.registerListener(PacketType::AssetUploadReply, this, "handleAssetUploadReply");
}
AssetRequest* AssetClient::create(QString hash) {
AssetRequest* AssetClient::createRequest(QString hash) {
if (QThread::currentThread() != thread()) {
AssetRequest* req;
QMetaObject::invokeMethod(this, "create",
@ -46,15 +47,32 @@ AssetRequest* AssetClient::create(QString hash) {
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (assetServer) {
auto assetClient = DependencyManager::get<AssetClient>();
auto request = new AssetRequest(assetClient.data(), hash);
return request;
return new AssetRequest(this, hash);
}
return nullptr;
}
AssetUpload* AssetClient::createUpload(QString filename) {
if (QThread::currentThread() != thread()) {
AssetUpload* upload;
QMetaObject::invokeMethod(this, "createUpload",
Qt::BlockingQueuedConnection,
Q_RETURN_ARG(AssetUpload*, upload),
Q_ARG(QString, filename));
return upload;
}
auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (assetServer) {
return new AssetUpload(this, filename);
}
return nullptr;
}
bool AssetClient::getAsset(QString hash, DataOffset start, DataOffset end, ReceivedAssetCallback callback) {
if (hash.length() != HASH_HEX_LENGTH) {
qDebug() << "Invalid hash size";
@ -156,6 +174,7 @@ void AssetClient::handleAssetGetReply(QSharedPointer<NLPacketList> packetList, S
bool AssetClient::uploadAsset(QByteArray data, QString extension, UploadResultCallback callback) {
auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (assetServer) {
auto packetList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::AssetUpload, QByteArray(), true, true));

View file

@ -21,6 +21,7 @@
#include "NLPacket.h"
class AssetRequest;
class AssetUpload;
struct AssetInfo {
QString hash;
@ -36,11 +37,8 @@ class AssetClient : public QObject, public Dependency {
public:
AssetClient();
Q_INVOKABLE AssetRequest* create(QString hash);
bool getAssetInfo(QString hash, GetInfoCallback callback);
bool getAsset(QString hash, DataOffset start, DataOffset end, ReceivedAssetCallback callback);
bool uploadAsset(QByteArray data, QString extension, UploadResultCallback callback);
bool abortDataRequest(MessageID messageID);
Q_INVOKABLE AssetRequest* createRequest(QString hash);
Q_INVOKABLE AssetUpload* createUpload(QString filename);
private slots:
void handleAssetGetInfoReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
@ -48,10 +46,18 @@ private slots:
void handleAssetUploadReply(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode);
private:
bool getAssetInfo(QString hash, GetInfoCallback callback);
bool getAsset(QString hash, DataOffset start, DataOffset end, ReceivedAssetCallback callback);
bool uploadAsset(QByteArray data, QString extension, UploadResultCallback callback);
bool abortDataRequest(MessageID messageID);
static MessageID _currentID;
QHash<MessageID, ReceivedAssetCallback> _pendingRequests;
QHash<MessageID, GetInfoCallback> _pendingInfoRequests;
QHash<MessageID, UploadResultCallback> _pendingUploads;
friend class AssetRequest;
friend class AssetUpload;
};
#endif

View file

@ -18,7 +18,7 @@ void ATPResourceRequest::doSend() {
auto assetClient = DependencyManager::get<AssetClient>();
auto hash = _url.path();
auto request = assetClient->create(hash);
auto request = assetClient->createRequest(hash);
if (!request) {
return;

View file

@ -0,0 +1,53 @@
//
// AssetUpload.cpp
// libraries/networking/src
//
// Created by Stephen Birarda on 2015-08-26.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AssetUpload.h"
#include <QtCore/QFileInfo>
#include <QtCore/QThread>
#include "AssetClient.h"
AssetUpload::AssetUpload(QObject* object, const QString& filename) :
_filename(filename)
{
}
void AssetUpload::start() {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "start", Qt::AutoConnection);
return;
}
// try to open the file at the given filename
QFile file { _filename };
if (file.open(QIODevice::ReadOnly)) {
// file opened, read the data and grab the extension
auto extension = QFileInfo(_filename).suffix();
auto data = file.readAll();
// ask the AssetClient to upload the asset and emit the proper signals from the passed callback
auto assetClient = DependencyManager::get<AssetClient>();
assetClient->uploadAsset(data, extension, [this](bool result, QString hash){
if (result) {
// successful upload - emit finished with a point to ourselves and the resulting hash
emit finished(this, hash);
} else {
}
});
}
}

View file

@ -0,0 +1,47 @@
//
// AssetUpload.h
// libraries/networking/src
//
// Created by Stephen Birarda on 2015-08-26.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#ifndef hifi_AssetUpload_h
#define hifi_AssetUpload_h
#include <QtCore/QObject>
// You should be able to upload an asset from any thread, and handle the responses in a safe way
// on your own thread. Everything should happen on AssetClient's thread, the caller should
// receive events by connecting to signals on an object that lives on AssetClient's threads.
class AssetUpload : public QObject {
Q_OBJECT
public:
enum Result {
Success = 0,
Timeout,
TooLarge,
};
AssetUpload(QObject* parent, const QString& filename);
Q_INVOKABLE void start();
const QString& getFilename() const { return _filename; }
signals:
void finished(AssetUpload* upload, const QString& hash);
void progress(uint64_t totalReceived, uint64_t total);
private:
QString _filename;
};
#endif // hifi_AssetUpload_h

View file

@ -24,4 +24,6 @@ enum AssetServerError : uint8_t {
INVALID_BYTE_RANGE,
};
const QString ATP_SCHEME = "atp";
#endif