1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

All: Handle case where file is left half-uploaded on Nextcloud instance (possibly an ocloud.de issue only)

This commit is contained in:
Laurent Cozic 2018-01-30 20:10:36 +00:00
parent 2805ae2acf
commit 7b760d03ef
4 changed files with 56 additions and 7 deletions

View File

@ -161,6 +161,8 @@ class WebDavApi {
let response = null;
// console.info('WebDAV', method + ' ' + path, headers, options);
if (options.source == 'file' && (method == 'POST' || method == 'PUT')) {
response = await shim.uploadBlob(url, fetchOptions);
} else if (options.target == 'string') {

View File

@ -4,6 +4,8 @@ const { basicDelta } = require('lib/file-api');
const { rtrimSlashes, ltrimSlashes } = require('lib/path-utils.js');
const Entities = require('html-entities').AllHtmlEntities;
const html_entity_decode = (new Entities()).decode;
const { shim } = require('lib/shim');
const { basename } = require('lib/path-utils');
class FileApiDriverWebDav {
@ -20,6 +22,7 @@ class FileApiDriverWebDav {
const result = await this.api().execPropFind(path, 0, [
'd:getlastmodified',
'd:resourcetype',
'd:getcontentlength', // Remove this once PUT call issue is sorted out
]);
const resource = this.api().objectFromJson(result, ['d:multistatus', 'd:response', 0]);
@ -34,6 +37,9 @@ class FileApiDriverWebDav {
const isCollection = this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:resourcetype', 0, 'd:collection', 0]);
const lastModifiedString = this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getlastmodified', 0]);
const sizeDONOTUSE = Number(this.api().stringFromJson(resource, ['d:propstat', 0, 'd:prop', 0, 'd:getcontentlength', 0]));
if (isNaN(sizeDONOTUSE)) throw new Error('Cannot get content size: ' + JSON.stringify(resource));
if (!lastModifiedString) throw new Error('Could not get lastModified date: ' + JSON.stringify(resource));
const lastModifiedDate = new Date(lastModifiedString);
@ -44,6 +50,7 @@ class FileApiDriverWebDav {
created_time: lastModifiedDate.getTime(),
updated_time: lastModifiedDate.getTime(),
isDir: isCollection === '',
sizeDONOTUSE: sizeDONOTUSE, // This property is used only for the WebDAV PUT hack (see below) so mark it as such so that it can be removed with the hack later on.
};
}
@ -229,7 +236,32 @@ class FileApiDriverWebDav {
}
async put(path, content, options = null) {
await this.api().exec('PUT', path, content, null, options);
// In theory, if a client doesn't complete an upload, the file will not appear in the Nextcloud app. Likewise if
// the server interrupts the upload midway, the client should receive some kind of error and try uploading the
// file again next time. At the very least the file should not appear half-uploaded on the server. In practice
// however it seems some files might end up half uploaded on the server (at least on ocloud.de) so, for now,
// instead of doing a simple PUT, we do it to a temp file on Nextcloud, then check the file size and, if it
// matches, move it its actual place (hoping the server won't mess up and only copy half of the file).
// This is innefficient so once the bug is better understood it should hopefully be possible to go back to
// using a single PUT call.
let contentSize = 0;
if (content) contentSize = content.length;
if (options && options.path) {
const stat = await shim.fsDriver().stat(options.path);
contentSize = stat.size;
}
const tempPath = this.fileApi_.tempDirName() + '/' + basename(path) + '_' + Date.now();
await this.api().exec('PUT', tempPath, content, null, options);
const stat = await this.stat(tempPath);
if (stat.sizeDONOTUSE != contentSize) {
// await this.delete(tempPath);
throw new Error('WebDAV PUT - Size check failed for ' + tempPath + ' Expected: ' + contentSize + '. Found: ' + stat.sizeDONOTUSE);
}
await this.move(tempPath, path);
}
async delete(path) {
@ -241,7 +273,9 @@ class FileApiDriverWebDav {
}
async move(oldPath, newPath) {
throw new Error('Not implemented');
await this.api().exec('MOVE', oldPath, null, {
'Destination': this.api().baseUrl() + '/' + newPath,
});
}
format() {

View File

@ -12,6 +12,17 @@ class FileApi {
this.driver_ = driver;
this.logger_ = new Logger();
this.syncTargetId_ = null;
this.tempDirName_ = null;
this.driver_.fileApi_ = this;
}
tempDirName() {
if (this.tempDirName_ === null) throw Error('Temp dir not set!');
return this.tempDirName_;
}
setTempDirName(v) {
this.tempDirName_ = v;
}
fsDriver() {
@ -40,9 +51,10 @@ class FileApi {
}
fullPath_(path) {
let output = this.baseDir_;
if (path != '') output += '/' + path;
return output;
let output = [];
if (this.baseDir_) output.push(this.baseDir_);
if (path) output.push(path);
return output.join('/');
}
// DRIVER MUST RETURN PATHS RELATIVE TO `path`

View File

@ -18,7 +18,7 @@ class Synchronizer {
this.state_ = 'idle';
this.db_ = db;
this.api_ = api;
//this.syncDirName_ = '.sync';
this.syncDirName_ = '.sync';
this.resourceDirName_ = '.resource';
this.logger_ = new Logger();
this.appType_ = appType;
@ -195,7 +195,8 @@ class Synchronizer {
this.logSyncOperation('starting', null, null, 'Starting synchronisation to target ' + syncTargetId + '... [' + synchronizationId + ']');
try {
//await this.api().mkdir(this.syncDirName_);
await this.api().mkdir(this.syncDirName_);
this.api().setTempDirName(this.syncDirName_);
await this.api().mkdir(this.resourceDirName_);
let donePaths = [];