SFTP support download. listDIr() support rollback.

This commit is contained in:
Junyuan Feng
2022-04-04 15:42:56 +08:00
parent f0081e0587
commit f8201f9542
8 changed files with 113 additions and 29 deletions

View File

@@ -4,6 +4,8 @@ PODS:
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_icmp_ping (0.0.1): - flutter_icmp_ping (0.0.1):
- Flutter - Flutter
- open_file (0.0.1):
- Flutter
- path_provider_ios (0.0.1): - path_provider_ios (0.0.1):
- Flutter - Flutter
- r_upgrade (0.0.1): - r_upgrade (0.0.1):
@@ -15,6 +17,7 @@ DEPENDENCIES:
- countly_flutter (from `.symlinks/plugins/countly_flutter/ios`) - countly_flutter (from `.symlinks/plugins/countly_flutter/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_icmp_ping (from `.symlinks/plugins/flutter_icmp_ping/ios`) - flutter_icmp_ping (from `.symlinks/plugins/flutter_icmp_ping/ios`)
- open_file (from `.symlinks/plugins/open_file/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- r_upgrade (from `.symlinks/plugins/r_upgrade/ios`) - r_upgrade (from `.symlinks/plugins/r_upgrade/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@@ -26,6 +29,8 @@ EXTERNAL SOURCES:
:path: Flutter :path: Flutter
flutter_icmp_ping: flutter_icmp_ping:
:path: ".symlinks/plugins/flutter_icmp_ping/ios" :path: ".symlinks/plugins/flutter_icmp_ping/ios"
open_file:
:path: ".symlinks/plugins/open_file/ios"
path_provider_ios: path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios" :path: ".symlinks/plugins/path_provider_ios/ios"
r_upgrade: r_upgrade:
@@ -37,6 +42,7 @@ SPEC CHECKSUMS:
countly_flutter: 38419412e193a1faa5babeb5d28a63fda260687d countly_flutter: 38419412e193a1faa5babeb5d28a63fda260687d
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_icmp_ping: 07e508847df7fa9262d050bb0b203de074bbe517 flutter_icmp_ping: 07e508847df7fa9262d050bb0b203de074bbe517
open_file: 02eb5cb6b21264bd3a696876f5afbfb7ca4f4b7d
path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5 path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5
r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114 r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114
url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af

View File

@@ -354,7 +354,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 101; CURRENT_PROJECT_VERSION = 108;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -362,7 +362,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.101; MARKETING_VERSION = 1.0.108;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -484,7 +484,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 101; CURRENT_PROJECT_VERSION = 108;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -492,7 +492,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.101; MARKETING_VERSION = 1.0.108;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -508,7 +508,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 101; CURRENT_PROJECT_VERSION = 108;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -516,7 +516,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.101; MARKETING_VERSION = 1.0.108;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";

View File

@@ -2,9 +2,9 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 107; static const int build = 108;
static const String engine = static const String engine =
"Flutter 2.10.3 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision 7e9793dee1 (8 days ago) • 2022-03-02 11:23:12 -0600\nEngine • revision bd539267b4\nTools • Dart 2.16.1 • DevTools 2.9.2\n"; "Flutter 2.10.3 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision 7e9793dee1 (8 days ago) • 2022-03-02 11:23:12 -0600\nEngine • revision bd539267b4\nTools • Dart 2.16.1 • DevTools 2.9.2\n";
static const String buildAt = "2022-03-10 13:25:24.362670"; static const String buildAt = "2022-03-10 15:25:32.032568";
static const int modifications = 11; static const int modifications = 0;
} }

View File

@@ -18,8 +18,8 @@ class PingPage extends StatefulWidget {
class _PingPageState extends State<PingPage> class _PingPageState extends State<PingPage>
with AutomaticKeepAliveClientMixin { with AutomaticKeepAliveClientMixin {
late TextEditingController _textEditingController; late TextEditingController _textEditingController;
String _result = '';
Ping? _ping; Ping? _ping;
late MediaQueryData _media;
@override @override
void initState() { void initState() {
@@ -33,6 +33,7 @@ class _PingPageState extends State<PingPage>
@override @override
void didChangeDependencies() { void didChangeDependencies() {
super.didChangeDependencies(); super.didChangeDependencies();
_media = MediaQuery.of(context);
} }
@override @override
@@ -46,13 +47,12 @@ class _PingPageState extends State<PingPage>
const SizedBox(height: 13), const SizedBox(height: 13),
buildInput(context, _textEditingController, maxLines: 1), buildInput(context, _textEditingController, maxLines: 1),
_buildControl(), _buildControl(),
RoundRectCard( RoundRectCard(ConstrainedBox(
SizedBox( constraints: BoxConstraints(
width: double.infinity, minWidth: double.infinity,
child: Padding( minHeight: _media.size.height * 0.6,
padding: const EdgeInsets.all(7), child: Text(_result)),
),
), ),
)),
])), ])),
onTap: () => FocusScope.of(context).requestFocus(FocusNode()), onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
), ),
@@ -60,12 +60,10 @@ class _PingPageState extends State<PingPage>
} }
void doPing() { void doPing() {
_result = '';
_ping = Ping(_textEditingController.text.trim()); _ping = Ping(_textEditingController.text.trim());
_ping!.stream.listen((event) { _ping!.stream.listen((event) {
final resp = event.response.toString(); final resp = event.response.toString();
if (resp == 'null') return; if (resp == 'null') return;
_result += '$resp\n';
setState(() {}); setState(() {});
}); });
} }

View File

@@ -1,5 +1,8 @@
import 'dart:io';
import 'package:dartssh2/dartssh2.dart'; import 'package:dartssh2/dartssh2.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:toolbox/core/utils.dart'; import 'package:toolbox/core/utils.dart';
import 'package:toolbox/data/model/server/server_connection_state.dart'; import 'package:toolbox/data/model/server/server_connection_state.dart';
import 'package:toolbox/data/model/server/server_private_info.dart'; import 'package:toolbox/data/model/server/server_private_info.dart';
@@ -7,6 +10,7 @@ import 'package:toolbox/data/model/sftp/absolute_path.dart';
import 'package:toolbox/data/model/sftp/sftp_side_status.dart'; import 'package:toolbox/data/model/sftp/sftp_side_status.dart';
import 'package:toolbox/data/provider/server.dart'; import 'package:toolbox/data/provider/server.dart';
import 'package:toolbox/locator.dart'; import 'package:toolbox/locator.dart';
import 'package:toolbox/view/widget/center_loading.dart';
import 'package:toolbox/view/widget/fade_in.dart'; import 'package:toolbox/view/widget/fade_in.dart';
import 'package:toolbox/view/widget/two_line_text.dart'; import 'package:toolbox/view/widget/two_line_text.dart';
@@ -144,6 +148,11 @@ class _SFTPPageState extends State<SFTPPage> {
title: const Text('Rename'), title: const Text('Rename'),
onTap: () => rename(context, file), onTap: () => rename(context, file),
), ),
ListTile(
leading: const Icon(Icons.download),
title: const Text('Download'),
onTap: () => download(context, file),
)
], ],
), ),
[ [
@@ -153,6 +162,42 @@ class _SFTPPageState extends State<SFTPPage> {
]); ]);
} }
void download(BuildContext context, SftpName name) {
showRoundDialog(
context, 'Download', Text('Download ${name.filename} to local?'), [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel')),
TextButton(
onPressed: () async {
var result = '';
try {
Navigator.of(context).pop();
showRoundDialog(context, name.filename, centerSizedLoading, [],
barrierDismiss: false);
final path = await getApplicationDocumentsDirectory();
final localFile = File('${path.path}/${name.filename}');
final remotePath = _status.path!.path + '/' + name.filename;
final file = await _status.client?.open(remotePath);
localFile.writeAsBytes(await file!.readBytes());
Navigator.of(context).pop();
} catch (e) {
result = e.toString();
} finally {
if (result.isEmpty) {
result = 'Donwloaded successfully.';
}
showRoundDialog(context, 'Result', Text(result), [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'))
]);
}
},
child: const Text('Download'))
]);
}
void delete(BuildContext context, SftpName file) { void delete(BuildContext context, SftpName file) {
Navigator.of(context).pop(); Navigator.of(context).pop();
showRoundDialog( showRoundDialog(
@@ -274,6 +319,7 @@ class _SFTPPageState extends State<SFTPPage> {
final sftpc = await client.sftp(); final sftpc = await client.sftp();
_status.client = sftpc; _status.client = sftpc;
} }
try {
final fs = final fs =
await _status.client!.listdir(path ?? (_status.path?.path ?? '/')); await _status.client!.listdir(path ?? (_status.path?.path ?? '/'));
fs.sort((a, b) => a.filename.compareTo(b.filename)); fs.sort((a, b) => a.filename.compareTo(b.filename));
@@ -284,6 +330,16 @@ class _SFTPPageState extends State<SFTPPage> {
_status.isBusy = false; _status.isBusy = false;
}); });
} }
} catch (e) {
await showRoundDialog(context, 'Error', Text(e.toString()), [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'))
]);
if (_status.path!.undo()) {
await listDir();
}
}
} }
Widget _buildDestSelector() { Widget _buildDestSelector() {

View File

@@ -1,3 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
const centerLoading = Center(child: CircularProgressIndicator()); const centerLoading = Center(child: CircularProgressIndicator());
const centerSizedLoading = SizedBox(
width: 77,
height: 77,
child: Center(
child: CircularProgressIndicator(),
),
);

View File

@@ -335,6 +335,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.11" version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.3"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@@ -349,6 +356,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
open_file:
dependency: "direct main"
description:
name: open_file
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.1"
path: path:
dependency: transitive dependency: transitive
description: description:
@@ -357,7 +371,7 @@ packages:
source: hosted source: hosted
version: "1.8.0" version: "1.8.0"
path_provider: path_provider:
dependency: transitive dependency: "direct main"
description: description:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
@@ -521,7 +535,7 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.3" version: "0.4.8"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:

View File

@@ -58,6 +58,8 @@ dependencies:
dart_ping_ios: ^1.0.0 dart_ping_ios: ^1.0.0
dropdown_button2: ^1.1.1 dropdown_button2: ^1.1.1
flutter_advanced_drawer: ^1.3.0 flutter_advanced_drawer: ^1.3.0
open_file: ^3.2.1
path_provider: ^2.0.9
dev_dependencies: dev_dependencies: