diff --git a/packages/nextcloud/lib/src/webdav/client.dart b/packages/nextcloud/lib/src/webdav/client.dart index 108ec64f..3f67d7ca 100644 --- a/packages/nextcloud/lib/src/webdav/client.dart +++ b/packages/nextcloud/lib/src/webdav/client.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; @@ -169,6 +170,33 @@ class WebDavClient { ), ); + /// Puts a new file at [path] with [file] as content. + /// + /// [lastModified] sets the date when the file was last modified on the server. + /// [created] sets the date when the file was created on the server. + /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PUT for more information. + Future putFile( + final File file, + final FileStat fileStat, + final String path, { + final DateTime? lastModified, + final DateTime? created, + final Function(double progres)? onProgress, + }) async { + var uploaded = 0; + return putStream( + file.openRead().map((final chunk) { + uploaded += chunk.length; + onProgress?.call(uploaded / fileStat.size * 100); + return Uint8List.fromList(chunk); + }), + path, + lastModified: lastModified, + created: created, + contentLength: fileStat.size, + ); + } + /// Gets the content of the file at [path]. Future get(final String path) async => (await getStream(path)).bodyBytes; @@ -178,6 +206,32 @@ class WebDavClient { _constructPath(path), ); + /// Gets the content of the file at [path]. + Future getFile( + final String path, + final File file, { + final Function(double progress)? onProgress, + }) async { + final sink = file.openWrite(); + final response = await getStream(path); + if (response.contentLength > 0) { + final completer = Completer(); + var downloaded = 0; + + response.listen((final chunk) async { + sink.add(chunk); + downloaded += chunk.length; + onProgress?.call(downloaded / response.contentLength * 100); + if (downloaded >= response.contentLength) { + completer.complete(); + } + }); + await completer.future; + } + + await sink.close(); + } + /// Retrieves the props for the resource at [path]. /// /// Optionally populates the given [prop]s on the returned files. diff --git a/packages/nextcloud/test/files/test.txt b/packages/nextcloud/test/files/test.txt deleted file mode 100644 index bbf5fc92..00000000 --- a/packages/nextcloud/test/files/test.txt +++ /dev/null @@ -1 +0,0 @@ -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum diff --git a/packages/nextcloud/test/webdav_test.dart b/packages/nextcloud/test/webdav_test.dart index 13e466dc..37527a22 100644 --- a/packages/nextcloud/test/webdav_test.dart +++ b/packages/nextcloud/test/webdav_test.dart @@ -2,6 +2,7 @@ library webdav_test; import 'dart:convert'; +import 'dart:io'; import 'dart:math'; import 'dart:typed_data'; @@ -196,17 +197,21 @@ void main() { }); test('Set properties', () async { + final lastModifiedDate = DateTime.utc(1972, 3); final createdDate = DateTime.utc(1971, 2); - final createdEpoch = createdDate.millisecondsSinceEpoch ~/ 1000; final uploadTime = DateTime.now(); - await client.webdav.put(Uint8List.fromList(utf8.encode('test')), 'test.txt'); + await client.webdav.put( + Uint8List.fromList(utf8.encode('test')), + 'test.txt', + lastModified: lastModifiedDate, + created: createdDate, + ); final updated = await client.webdav.proppatch( 'test.txt', set: WebDavProp( ocfavorite: 1, - nccreationtime: createdEpoch, ), ); expect(updated, isTrue); @@ -215,6 +220,7 @@ void main() { 'test.txt', prop: WebDavPropWithoutValues.fromBools( ocfavorite: true, + davgetlastmodified: true, nccreationtime: true, ncuploadtime: true, ), @@ -225,6 +231,7 @@ void main() { .first .prop; expect(props.ocfavorite, 1); + expect(webdavDateFormat.parseUtc(props.davgetlastmodified!), lastModifiedDate); expect(DateTime.fromMillisecondsSinceEpoch(props.nccreationtime! * 1000).isAtSameMomentAs(createdDate), isTrue); expectDateInReasonableTimeRange(DateTime.fromMillisecondsSinceEpoch(props.ncuploadtime! * 1000), uploadTime); }); @@ -277,6 +284,29 @@ void main() { expect(props.ocfavorite, 0); }); + test('Upload and download file', () async { + final destinationDir = Directory.systemTemp.createTempSync(); + final destination = File('${destinationDir.path}/test.png'); + final source = File('test/files/test.png'); + final progressValues = []; + + await client.webdav.putFile( + source, + source.statSync(), + 'test.png', + onProgress: progressValues.add, + ); + await client.webdav.getFile( + 'test.png', + destination, + onProgress: progressValues.add, + ); + expect(progressValues, [100.0, 100.0]); + expect(destination.readAsBytesSync(), source.readAsBytesSync()); + + destinationDir.deleteSync(recursive: true); + }); + group('litmus', () { group('basic', () { test('options', () async {