import 'dart:io'; import 'package:archive/archive_io.dart'; import 'package:cloud_archiver/config.dart'; import 'package:cloud_archiver/state.dart'; import 'package:filesize/filesize.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:webdav_client/webdav_client.dart' as dav; late dav.Client _client; String totalSize() { if (cloudArchives.value.isEmpty) return '0 MB'; return filesize( cloudArchives.value.values .map((archives) => archives.map((archive) => archive.size ?? 0).reduce((total, size) => total + size)) .reduce((total, size) => total + size), 1, ); } Future connect(String server, String user, String auth) async { var client = dav.newClient( server, debug: true, user: user, password: auth, ); client.setHeaders({'accept-charset': 'utf-8'}); client.setConnectTimeout(5 * 1000); // Set send data timeout time in milliseconds. client.setSendTimeout(60 * 1000); // Set transfer data time in milliseconds. client.setReceiveTimeout(60 * 1000); try { davStatus.value = DavStatus.loading; await client.ping(); davStatus.value = DavStatus.connected; _client = client; return true; } catch (e) { print('$e'); davStatus.value = DavStatus.notConnected; } return false; } void loadLocalStatus() { if (appConfig?.syncItems.isNotEmpty != true) return; var result = {}; for (var syncItem in appConfig!.syncItems) { var file = File(syncItem.path); var stat = file.statSync(); switch (stat.type) { case FileSystemEntityType.file: result[syncItem] = stat.modified; case FileSystemEntityType.directory: result[syncItem] = dirModifiedTime(syncItem.path); default: result[syncItem] = null; } } localSyncItems.value = { ...localSyncItems.value, ...result, }; } DateTime dirModifiedTime(String dir) { var directory = Directory(dir); var modified = directory.statSync().modified; for (var entry in Directory(dir).listSync(recursive: true)) { var stat = entry.statSync(); if (stat.type == FileSystemEntityType.file) { if (stat.modified.isAfter(modified)) { modified = stat.modified; } } } return modified; } void loadCloudArchives() async { if (davStatus.value != DavStatus.connected) return; davStatus.value = DavStatus.loading; List list = []; try { list = await _client.readDir('/'); } catch (e) { print(e); davStatus.value = DavStatus.notConnected; return; } var result = >{}; for (var item in list) { if (item.isDir == true && item.path?.isNotEmpty == true) { var files = await _client.readDir(item.path!); result[basename(item.path!)] = files; } } cloudArchives.value = result; davStatus.value = DavStatus.connected; } Future upload(SyncItemConfig item, bool needCreateDir) async { if (needCreateDir) { await _client.mkdir(item.name); } var file = File(item.path); var cacheDir = await getApplicationCacheDirectory(); var zipFileEncoder = ZipFileEncoder(); var zipFileName = '${appConfig?.client ?? 'DEBUG'}-${DateTime.now().strFileName}.zip'; var zipTempFilePath = join(cacheDir.path, zipFileName); zipFileEncoder.create(zipTempFilePath); if (file.statSync().type == FileSystemEntityType.directory) { await zipFileEncoder.addDirectory(Directory(item.path)); } else { await zipFileEncoder.addFile(file); } await zipFileEncoder.close(); await _client.writeFromFile(zipTempFilePath, join(item.name, zipFileName)); File(zipTempFilePath).delete(); loadCloudArchives(); } Future download(SyncItemConfig item, dav.File cloudArchive) async { print('Start download: ${cloudArchive.path}'); var cacheDir = await getApplicationCacheDirectory(); var zipPath = join(cacheDir.path, '${item.name}_${cloudArchive.name}'); await _client.read2File(cloudArchive.path!, zipPath); await extractFileToDisk(zipPath, cacheDir.path); File(zipPath).delete(); var temp = File(join(cacheDir.path, basename(item.path))); print('Temp path: ${temp.path}'); var fileSystemEntityType = temp.statSync().type; switch (fileSystemEntityType) { case FileSystemEntityType.directory: var directory = Directory(item.path); if (directory.existsSync()) { directory.deleteSync(recursive: true); } Directory(temp.path); Directory(temp.path).renameSync(item.path); case FileSystemEntityType.file: var file = File(item.path); if (file.existsSync()) { file.deleteSync(); } temp.renameSync(item.path); default: print('Failed: $fileSystemEntityType'); } } extension DateTimeExt on DateTime { String get strHm => '$year/${padZero(month)}/${padZero(day)} ${padZero(hour)}:${padZero(minute)}'; String get strHms => '$strHm:${padZero(second)}'; String get strFileName => '${year}_${padZero(month)}_${padZero(day)}_${padZero(hour)}_${padZero(minute)}_${padZero(second)}'; String padZero(int num, [fix = 2]) => num.toString().padLeft(fix, '0'); }