/******************************************************************************************** * * * Copyright (C) 2017 Armin Felder, Dennis Beier * * This file is part of RocketChatMobileEngine <https://git.fairkom.net/chat/fairchat>. * * * * RocketChatMobileEngine is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * RocketChatMobileEngine is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with RocketChatMobileEngine. If not, see <http://www.gnu.org/licenses/>. * * * ********************************************************************************************/ #include "fileuploader.h" FileUploader::FileUploader( QObject *parent, RocketChatServerData *pServer, const QUrl &pUri, const QString &pChannelId ): QObject( parent ), mUri( pUri ), mChannelId( pChannelId ), mServer( pServer ) { auto file = QSharedPointer<QFile>::create( pUri.toLocalFile() ); if ( file->isOpen() ) { file->close(); } if ( file->open( QIODevice::ReadOnly ) ) { mDump = file->readAll(); this->mSize = mDump.size(); qDebug() << "read file with bytes:" << file->size(); if ( !this->mSize ) { mErrorStatus = true; } else { file->close(); QMimeDatabase db; mType = db.mimeTypeForFile( pUri.fileName() ); } } else { qWarning() << file->errorString(); } } void FileUploader::upload( const std::function<void( void )> &pCleanup ) { this->mCleanUp = pCleanup; DdpCallback success = [ & ]( QJsonObject response, MeteorDDP * ddp ) { Q_UNUSED( ddp ); onUfsCreated( response ); }; DdpCallback errorFunc = [ & ]( QJsonObject response, MeteorDDP * ddp ) { Q_UNUSED( ddp ); if ( response.contains( "error" ) ) { QJsonObject errorObject = response["error"].toObject(); if ( errorObject.contains( "message" ) ) { QString message = errorObject["message"].toString(); emit error( message ); } } }; QSharedPointer<DDPUfsCreateRequest> request( new DDPUfsCreateRequest( mChannelId, mUri.fileName(), mDump.size(), mType.name(), success ) ); request->setError( errorFunc ); if ( !mErrorStatus ) { mServer->sendDdprequest( request, true ); } } void FileUploader::cancel() { QMutexLocker locker( &mLock ); mCanceled = true; if ( mPartialRequests.empty() ) { mCleanUp(); } } void FileUploader::onUfsCreated( const QJsonObject &pResponse ) { if ( pResponse.contains( "result" ) ) { QJsonObject result = pResponse["result"].toObject(); if ( result.contains( "url" ) && result.contains( "token" ) && result.contains( "fileId" ) ) { QString url = result["url"].toString(); mToken = result["token"].toString(); qDebug() << result.keys(); qDebug() << result["fileId"].toString(); QString fileId = result["fileId"].toString(); this->mFileId = fileId; int pos = 0; int arrsize = mSize; int sizeInArray = 100000; emit ufsCreated( fileId ); int chunkNum = arrsize / sizeInArray; chunkNum += ( arrsize % sizeInArray ) ? 1 : 0; qDebug() << "creating partials: " << chunkNum << "of bytes:" << mSize; if ( mSize == 0 ) { throw std::logic_error( "Zero byte file!" ); } bool cancel = mCanceled; RestRequestCallback nextOne = [ = ]( QNetworkReply *, QJsonObject data, RestApi * ) { Q_UNUSED( data ); QMutexLocker locker( &mLock ); if ( cancel ) { mPartialRequests.clear(); mCleanUp(); } else { nextRequest(); } }; for ( int i = 0; i < chunkNum; i++ ) { qDebug() << "creating partials nr " << chunkNum; QByteArray chunk = mDump.mid( pos, sizeInArray ); double progress = ( 1.0 / chunkNum ) * ( i + 1 ); QString completePath = url + "&progress=" + QString::number( progress ); auto partial = QSharedPointer<RestFileUploadRequest>::create( completePath, mToken, chunk ); partial->setProgress( progress ); qDebug() << "creating partial " << chunkNum << ":" << progress << "size:" << chunk.size();; partial->setSuccess( nextOne ); mPartialRequests.append( partial ); pos += chunk.size(); } auto lastRequest = mPartialRequests.last(); lastRequest->setSuccess( [ = ]( QNetworkReply * reply, QJsonObject data, RestApi * ) { Q_UNUSED( reply ); onUploadCompleted( data ); } ); auto request = mPartialRequests.first(); mServer->sendApiRequest( request, true ); } } } void FileUploader::onUploadCompleted( const QJsonObject &pResponse ) { Q_UNUSED( pResponse ); qDebug() << "upload completed"; QString store = QStringLiteral( "rocketchat_uploads" ); int major = std::get<0>( RocketChatServerConfig::serverVersion ); int minor = std::get<1>( RocketChatServerConfig::serverVersion ); int patch = std::get<2>( RocketChatServerConfig::serverVersion ); if ( major != -1 && minor != -1 && patch != -1 ) { if ( major >= 0 && minor > 60 ) { store = QStringLiteral( "Uploads" ); } } QJsonArray params = {mFileId, store, mToken}; DdpCallback success2 = [ = ]( QJsonObject pResponse, MeteorDDP * pDdp ) { Q_UNUSED( pDdp ); qDebug() << "ufs created response" << pResponse; if ( pResponse.contains( "result" ) ) { QJsonObject result = pResponse["result"].toObject(); QJsonArray params2 = {mChannelId, QJsonValue::Null, result}; DdpCallback success3 = [ = ]( QJsonObject pResponse, MeteorDDP * pDdp ) { Q_UNUSED( pResponse ); Q_UNUSED( pDdp ); qDebug() << "send file message response"; mCleanUp(); }; auto request3 = QSharedPointer<DDPMethodRequest>::create( "sendFileMessage", params2, success3 ); mServer->sendDdprequest( request3, true ); } }; auto request = QSharedPointer<DDPMethodRequest>::create( QStringLiteral( "ufsComplete" ), params, success2 ); mServer->sendDdprequest( request, true ); qDebug() << "file upload ready"; } void FileUploader::nextRequest() { if ( !mPartialRequests.isEmpty() ) { mPartialRequests.pop_front(); auto request = mPartialRequests.first(); double diff = request->getProgress() - progress; uint now = QDateTime::currentDateTime().toTime_t(); if( diff >= 0.05 && now - lastUpdate){ progress = request->getProgress(); qDebug() << "progress is "<< progress; emit progressChanged( progress ); lastUpdate = now; } mServer->sendApiRequest( request, true ); } } QString FileUploader::getFileId() const { return mFileId; }