From e300f043bb53733789d04aa2dd14412f95df4749 Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Thu, 20 May 2021 23:43:53 +0800 Subject: [PATCH] Support copying remote file --- lib/api/api.dart | 18 ++++++++++++ lib/entity/file.dart | 67 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/lib/api/api.dart b/lib/api/api.dart index 01b64c05..e043a781 100644 --- a/lib/api/api.dart +++ b/lib/api/api.dart @@ -335,5 +335,23 @@ class _Files { } } + /// A file or folder can be copied by sending a COPY request to the file or + /// folder and specifying the [destinationUrl] as full url + Future copy({ + @required String path, + @required String destinationUrl, + bool overwrite, + }) async { + try { + return await _api.request("COPY", path, header: { + "Destination": destinationUrl, + if (overwrite != null) "Overwrite": overwrite ? "T" : "F", + }); + } catch (e) { + _log.severe("[copy] Failed while delete", e); + rethrow; + } + } + static final _log = Logger("api.api._Files"); } diff --git a/lib/entity/file.dart b/lib/entity/file.dart index 92cc2c44..728a59eb 100644 --- a/lib/entity/file.dart +++ b/lib/entity/file.dart @@ -374,6 +374,20 @@ class FileRepo { Future updateMetadata(Account account, File file, Metadata metadata) => this.dataSrc.updateMetadata(account, file, metadata); + /// See [FileDataSource.copy] + Future copy( + Account account, + File f, + String destination, { + bool shouldOverwrite, + }) => + this.dataSrc.copy( + account, + f, + destination, + shouldOverwrite: shouldOverwrite, + ); + final FileDataSource dataSrc; } @@ -395,6 +409,17 @@ abstract class FileDataSource { /// This will completely replace the metadata of the file [f]. Partial update /// is not supported Future updateMetadata(Account account, File f, Metadata metadata); + + /// Copy [f] to [destination] + /// + /// [destination] should be a relative WebDAV path like + /// remote.php/dav/files/admin/new/location + Future copy( + Account account, + File f, + String destination, { + bool shouldOverwrite, + }); } class FileWebdavDataSource implements FileDataSource { @@ -509,6 +534,27 @@ class FileWebdavDataSource implements FileDataSource { } } + @override + copy( + Account account, + File f, + String destination, { + bool shouldOverwrite, + }) async { + _log.info("[copy] ${f.path} to $destination"); + final response = await Api(account).files().copy( + path: f.path, + destinationUrl: "${account.url}/$destination", + overwrite: shouldOverwrite, + ); + if (!response.isGood) { + _log.severe("[copy] Failed requesting sever: $response"); + throw ApiException( + response: response, + message: "Failed communicating with server: ${response.statusCode}"); + } + } + static final _log = Logger("entity.file.FileWebdavDataSource"); } @@ -574,6 +620,16 @@ class FileAppDbDataSource implements FileDataSource { }); } + @override + copy( + Account account, + File f, + String destination, { + bool shouldOverwrite, + }) async { + // do nothing + } + Future> _doList(ObjectStore store, Account account, File f) async { final index = store.index(AppDbFileEntry.indexName); final path = AppDbFileEntry.toPath(account, f); @@ -676,6 +732,17 @@ class FileCachedDataSource implements FileDataSource { .then((_) => _appDbSrc.updateMetadata(account, f, metadata)); } + @override + copy( + Account account, + File f, + String destination, { + bool shouldOverwrite, + }) async { + await _remoteSrc.copy(account, f, destination, + shouldOverwrite: shouldOverwrite); + } + Future _cacheResult(Account account, File f, List result) { return AppDb.use((db) async { final transaction = db.transaction(AppDb.fileStoreName, idbModeReadWrite);