void PropagateUploadFileNG::doStartUpload() { propagator()->_activeJobList.append(this); const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file); if (progressInfo._valid && progressInfo._modtime == _item->_modtime) { _transferId = progressInfo._transferid; auto url = chunkUrl(); auto job = new LsColJob(propagator()->account(), url, this); _jobs.append(job); job->setProperties(QList<QByteArray>() << "resourcetype" << "getcontentlength"); connect(job, &LsColJob::finishedWithoutError, this, &PropagateUploadFileNG::slotPropfindFinished); connect(job, &LsColJob::finishedWithError, this, &PropagateUploadFileNG::slotPropfindFinishedWithError); connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed); connect(job, &LsColJob::directoryListingIterated, this, &PropagateUploadFileNG::slotPropfindIterate); job->start(); return; } else if (progressInfo._valid) { // The upload info is stale. remove the stale chunks on the server _transferId = progressInfo._transferid; // Fire and forget. Any error will be ignored. (new DeleteJob(propagator()->account(), chunkUrl(), this))->start(); // startNewUpload will reset the _transferId and the UploadInfo in the db. } startNewUpload(); }
void PropagateUploadFileNG::startNewUpload() { ASSERT(propagator()->_activeJobList.count(this) == 1); _transferId = qrand() ^ _item->_modtime ^ (_item->_size << 16) ^ qHash(_item->_file); _sent = 0; _currentChunk = 0; propagator()->reportProgress(*_item, 0); SyncJournalDb::UploadInfo pi; pi._valid = true; pi._transferid = _transferId; pi._modtime = _item->_modtime; pi._contentChecksum = _item->_checksumHeader; propagator()->_journal->setUploadInfo(_item->_file, pi); propagator()->_journal->commit("Upload info"); QMap<QByteArray, QByteArray> headers; headers["OC-Total-Length"] = QByteArray::number(_item->_size); auto job = new MkColJob(propagator()->account(), chunkUrl(), headers, this); connect(job, SIGNAL(finished(QNetworkReply::NetworkError)), this, SLOT(slotMkColFinished(QNetworkReply::NetworkError))); connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed); job->start(); }
void PropagateUploadFileNG::slotPropfindIterate(const QString &name, const QMap<QString, QString> &properties) { if (name == chunkUrl().path()) { return; // skip the info about the path itself } bool ok = false; QString chunkName = name.mid(name.lastIndexOf('/') + 1); auto chunkId = chunkName.toUInt(&ok); if (ok) { ServerChunkInfo chunkinfo = { properties["getcontentlength"].toULongLong(), chunkName }; _serverChunks[chunkId] = chunkinfo; } }
void PropagateUploadFileNG::slotPropfindFinished() { auto job = qobject_cast<LsColJob *>(sender()); slotJobDestroyed(job); // remove it from the _jobs list propagator()->_activeJobList.removeOne(this); _currentChunk = 0; _sent = 0; while (_serverChunks.contains(_currentChunk)) { _sent += _serverChunks[_currentChunk].size; _serverChunks.remove(_currentChunk); ++_currentChunk; } if (_sent > _item->_size) { // Normally this can't happen because the size is xor'ed with the transfer id, and it is // therefore impossible that there is more data on the server than on the file. qCCritical(lcPropagateUpload) << "Inconsistency while resuming " << _item->_file << ": the size on the server (" << _sent << ") is bigger than the size of the file (" << _item->_size << ")"; startNewUpload(); return; } qCInfo(lcPropagateUpload) << "Resuming " << _item->_file << " from chunk " << _currentChunk << "; sent =" << _sent; if (!_serverChunks.isEmpty()) { qCInfo(lcPropagateUpload) << "To Delete" << _serverChunks.keys(); propagator()->_activeJobList.append(this); _removeJobError = false; // Make sure that if there is a "hole" and then a few more chunks, on the server // we should remove the later chunks. Otherwise when we do dynamic chunk sizing, we may end up // with corruptions if there are too many chunks, or if we abort and there are still stale chunks. for (auto it = _serverChunks.begin(); it != _serverChunks.end(); ++it) { auto job = new DeleteJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), it->originalName), this); QObject::connect(job, &DeleteJob::finishedSignal, this, &PropagateUploadFileNG::slotDeleteJobFinished); _jobs.append(job); job->start(); } _serverChunks.clear(); return; } startNextChunk(); }
void PropagateUploadFileNG::doStartUpload() { propagator()->_activeJobList.append(this); const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file); if (progressInfo._valid && Utility::qDateTimeToTime_t(progressInfo._modtime) == _item->_modtime) { _transferId = progressInfo._transferid; auto url = chunkUrl(); auto job = new LsColJob(propagator()->account(), url, this); _jobs.append(job); job->setProperties(QList<QByteArray>() << "resourcetype" << "getcontentlength"); connect(job, SIGNAL(finishedWithoutError()), this, SLOT(slotPropfindFinished())); connect(job, SIGNAL(finishedWithError(QNetworkReply *)), this, SLOT(slotPropfindFinishedWithError())); connect(job, SIGNAL(destroyed(QObject *)), this, SLOT(slotJobDestroyed(QObject *))); connect(job, SIGNAL(directoryListingIterated(QString, QMap<QString, QString>)), this, SLOT(slotPropfindIterate(QString, QMap<QString, QString>))); job->start(); return; } else if (progressInfo._valid) {
void PropagateUploadFileNG::startNextChunk() { if (propagator()->_abortRequested.fetchAndAddRelaxed(0)) return; quint64 fileSize = _item->_size; ENFORCE(fileSize >= _sent, "Sent data exceeds file size"); // prevent situation that chunk size is bigger then required one to send _currentChunkSize = qMin(propagator()->_chunkSize, fileSize - _sent); if (_currentChunkSize == 0) { Q_ASSERT(_jobs.isEmpty()); // There should be no running job anymore _finished = true; // Finish with a MOVE QString destination = QDir::cleanPath(propagator()->account()->url().path() + QLatin1Char('/') + propagator()->account()->davPath() + propagator()->_remoteFolder + _item->_file); auto headers = PropagateUploadFileCommon::headers(); // "If-Match applies to the source, but we are interested in comparing the etag of the destination auto ifMatch = headers.take("If-Match"); if (!ifMatch.isEmpty()) { headers["If"] = "<" + destination.toUtf8() + "> ([" + ifMatch + "])"; } if (!_transmissionChecksumHeader.isEmpty()) { qCInfo(lcPropagateUpload) << destination << _transmissionChecksumHeader; headers[checkSumHeaderC] = _transmissionChecksumHeader; } headers["OC-Total-Length"] = QByteArray::number(fileSize); auto job = new MoveJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), "/.file"), destination, headers, this); _jobs.append(job); connect(job, &MoveJob::finishedSignal, this, &PropagateUploadFileNG::slotMoveJobFinished); connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed); propagator()->_activeJobList.append(this); job->start(); return; } auto device = new UploadDevice(&propagator()->_bandwidthManager); const QString fileName = propagator()->getFilePath(_item->_file); if (!device->prepareAndOpen(fileName, _sent, _currentChunkSize)) { qCWarning(lcPropagateUpload) << "Could not prepare upload device: " << device->errorString(); // If the file is currently locked, we want to retry the sync // when it becomes available again. if (FileSystem::isFileLocked(fileName)) { emit propagator()->seenLockedFile(fileName); } // Soft error because this is likely caused by the user modifying his files while syncing abortWithError(SyncFileItem::SoftError, device->errorString()); return; } QMap<QByteArray, QByteArray> headers; headers["OC-Chunk-Offset"] = QByteArray::number(_sent); _sent += _currentChunkSize; QUrl url = chunkUrl(_currentChunk); // job takes ownership of device via a QScopedPointer. Job deletes itself when finishing PUTFileJob *job = new PUTFileJob(propagator()->account(), url, device, headers, _currentChunk, this); _jobs.append(job); connect(job, &PUTFileJob::finishedSignal, this, &PropagateUploadFileNG::slotPutFinished); connect(job, &PUTFileJob::uploadProgress, this, &PropagateUploadFileNG::slotUploadProgress); connect(job, &PUTFileJob::uploadProgress, device, &UploadDevice::slotJobUploadProgress); connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed); job->start(); propagator()->_activeJobList.append(this); _currentChunk++; }