fileuploader.cpp 8.42 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/********************************************************************************************
 *                                                                                          *
 * 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/>.           *
 *                                                                                          *
 ********************************************************************************************/



armin's avatar
armin committed
23
24
#include "fileuploader.h"

armin's avatar
as    
armin committed
25
26
FileUploader::FileUploader( QObject *parent, RocketChatServerData *pServer, const QUrl &pUri, const QString &pChannelId ): QObject( parent ),
    mUri( pUri ), mChannelId( pChannelId ), mServer( pServer )
armin's avatar
armin committed
27
{
armin's avatar
armin committed
28
#if defined ( Q_OS_ANDROID) || defined(Q_OS_IOS)
armin's avatar
armin committed
29
30
    auto file = QSharedPointer<QFile>::create( pUri.path() );
#else
Armin Felder's avatar
Armin Felder committed
31
    auto file = QSharedPointer<QFile>::create( pUri.toLocalFile() );
armin's avatar
armin committed
32
#endif
armin's avatar
armin committed
33
34
35
36
37

    if ( file->isOpen() ) {
        file->close();
    }

38
    if ( file->open( QIODevice::ReadOnly ) ) {
armin's avatar
armin committed
39
40
41
        mDump = file->readAll();
        this->mSize = mDump.size();
        qDebug() << "read file with bytes:" << file->size();
42
43

        if ( !this->mSize ) {
armin's avatar
armin committed
44
            mErrorStatus = true;
45
        } else {
armin's avatar
armin committed
46
47
48
49
            file->close();
            QMimeDatabase db;
            mType = db.mimeTypeForFile( pUri.fileName() );
        }
50
51
    } else {
        qWarning() << file->errorString();
armin's avatar
armin committed
52
53
54
    }
}

armin's avatar
cleanup    
armin committed
55
void FileUploader::upload( const std::function<void( void )> &pCleanup )
armin's avatar
armin committed
56
57
58
{
    this->mCleanUp = pCleanup;
    DdpCallback success = [ & ]( QJsonObject response, MeteorDDP * ddp ) {
59
        Q_UNUSED( ddp );
armin's avatar
armin committed
60
61
62
        onUfsCreated( response );
    };
    DdpCallback errorFunc = [ & ]( QJsonObject response, MeteorDDP * ddp ) {
63
64
65
        Q_UNUSED( ddp );

        if ( response.contains( "error" ) ) {
armin's avatar
armin committed
66
            QJsonObject errorObject = response["error"].toObject();
67
68

            if ( errorObject.contains( "message" ) ) {
armin's avatar
armin committed
69
                QString message = errorObject["message"].toString();
70
                emit error( message );
armin's avatar
armin committed
71
72
73
74
75
76
77
            }
        }
    };

    QSharedPointer<DDPUfsCreateRequest> request(
        new DDPUfsCreateRequest( mChannelId, mUri.fileName(), mDump.size(), mType.name(), success )
    );
78
79
80
81
    request->setError( errorFunc );

    if ( !mErrorStatus ) {
        mServer->sendDdprequest( request, true );
armin's avatar
armin committed
82
83
84
85
86
    }
}

void FileUploader::cancel()
{
87
    QMutexLocker locker( &mLock );
armin's avatar
as    
armin committed
88
    mCanceled = true;
89
90

    if ( mPartialRequests.empty() ) {
armin's avatar
armin committed
91
92
93
94
        mCleanUp();
    }
}

armin's avatar
cleanup    
armin committed
95
void FileUploader::onUfsCreated( const QJsonObject &pResponse )
armin's avatar
armin committed
96
{
97
    if ( pResponse.contains( "result" ) ) {
armin's avatar
armin committed
98
        QJsonObject result = pResponse["result"].toObject();
99
100

        if ( result.contains( "url" ) && result.contains( "token" ) && result.contains( "fileId" ) ) {
armin's avatar
armin committed
101
102
103
104
105
106
107
108
109
110
111
            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;
112
            chunkNum += ( arrsize % sizeInArray ) ? 1 : 0;
armin's avatar
armin committed
113
114
115
            qDebug() << "creating partials: " << chunkNum << "of bytes:" << mSize;

            if ( mSize == 0 ) {
armin's avatar
armin committed
116
                throw std::logic_error( "Zero byte file!" );
armin's avatar
armin committed
117
            }
118

armin's avatar
armin committed
119
120
            bool cancel = mCanceled;
            RestRequestCallback nextOne = [ = ]( QNetworkReply *, QJsonObject data, RestApi * ) {
121
122
123
124
                Q_UNUSED( data );
                QMutexLocker locker( &mLock );

                if ( cancel ) {
armin's avatar
armin committed
125
126
                    mPartialRequests.clear();
                    mCleanUp();
127
                } else {
armin's avatar
armin committed
128
129
130
131
132
133
134
135
136
                    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 );
armin's avatar
c++ 17    
armin committed
137
                auto partial = QSharedPointer<RestFileUploadRequest>::create( completePath, mToken, chunk );
armin's avatar
armin committed
138
139
140
141
142
143
144
145
146
                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 * ) {
147
                Q_UNUSED( reply );
armin's avatar
armin committed
148
149
150
                onUploadCompleted( data );
            } );
            auto request = mPartialRequests.first();
151
            mServer->sendApiRequest( request, true );
armin's avatar
armin committed
152
153
154
155
        }
    }
}

armin's avatar
cleanup    
armin committed
156
void FileUploader::onUploadCompleted( const QJsonObject &pResponse )
armin's avatar
armin committed
157
{
158
    Q_UNUSED( pResponse );
armin's avatar
armin committed
159
    qDebug() << "upload completed";
armin's avatar
cleanup    
armin committed
160
    QString store = QStringLiteral( "rocketchat_uploads" );
armin's avatar
armin committed
161
162
163
164
165
166
167
168
169
170
171
    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};
armin's avatar
ias    
armin committed
172
    DdpCallback success2 = [ = ]( QJsonObject pResponse, MeteorDDP * pDdp ) {
173
        Q_UNUSED( pDdp );
armin's avatar
armin committed
174
        qDebug() << "ufs created response" << pResponse;
175
176

        if ( pResponse.contains( "result" ) ) {
armin's avatar
armin committed
177
178
            QJsonObject result = pResponse["result"].toObject();
            QJsonArray params2 = {mChannelId, QJsonValue::Null, result};
armin's avatar
ias    
armin committed
179
            DdpCallback success3 = [ = ]( QJsonObject pResponse, MeteorDDP * pDdp ) {
180
181
                Q_UNUSED( pResponse );
                Q_UNUSED( pDdp );
armin's avatar
armin committed
182
183
184
                qDebug() << "send file message response";
                mCleanUp();
            };
armin's avatar
c++ 17    
armin committed
185
            auto request3 = QSharedPointer<DDPMethodRequest>::create( "sendFileMessage", params2, success3 );
186
            mServer->sendDdprequest( request3, true );
armin's avatar
armin committed
187
188
        }
    };
armin's avatar
c++ 17    
armin committed
189
    auto request = QSharedPointer<DDPMethodRequest>::create( QStringLiteral( "ufsComplete" ), params, success2 );
190
    mServer->sendDdprequest( request, true );
armin's avatar
armin committed
191
192
193
194
195
196
    qDebug() << "file upload ready";
}

void FileUploader::nextRequest()
{

armin's avatar
as    
armin committed
197
    if ( !mPartialRequests.isEmpty() ) {
armin's avatar
armin committed
198
199
        mPartialRequests.pop_front();
        auto request = mPartialRequests.first();
200
        double diff = request->getProgress() - progress;
201
        uint now = QDateTime::currentDateTime().toTime_t();
armin's avatar
armin committed
202
203

        if ( diff >= 0.05 && now - lastUpdate ) {
204
            progress = request->getProgress();
armin's avatar
armin committed
205
            qDebug() << "progress is " << progress;
206
            emit progressChanged( progress );
207
            lastUpdate = now;
208
        }
armin's avatar
armin committed
209

210
        mServer->sendApiRequest( request, true );
armin's avatar
armin committed
211
212
213
214
215
216
217
    }
}

QString FileUploader::getFileId() const
{
    return mFileId;
}