#144 new: sftp decompress

This commit is contained in:
lollipopkit
2023-08-26 23:07:16 +08:00
parent 7d4c30732a
commit 65de4a8ca5
14 changed files with 133 additions and 19 deletions

View File

@@ -332,6 +332,12 @@ abstract class S {
/// **'Decode'** /// **'Decode'**
String get decode; String get decode;
/// No description provided for @decompress.
///
/// In en, this message translates to:
/// **'Decompress'**
String get decompress;
/// No description provided for @delete. /// No description provided for @delete.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -121,6 +121,9 @@ class SDe extends S {
@override @override
String get decode => 'Decode'; String get decode => 'Decode';
@override
String get decompress => 'Dekomprimieren';
@override @override
String get delete => 'Löschen'; String get delete => 'Löschen';

View File

@@ -121,6 +121,9 @@ class SEn extends S {
@override @override
String get decode => 'Decode'; String get decode => 'Decode';
@override
String get decompress => 'Decompress';
@override @override
String get delete => 'Delete'; String get delete => 'Delete';

View File

@@ -121,6 +121,9 @@ class SId extends S {
@override @override
String get decode => 'Membaca sandi'; String get decode => 'Membaca sandi';
@override
String get decompress => 'Dekompresi';
@override @override
String get delete => 'Menghapus'; String get delete => 'Menghapus';

View File

@@ -121,6 +121,9 @@ class SZh extends S {
@override @override
String get decode => '解码'; String get decode => '解码';
@override
String get decompress => '解压缩';
@override @override
String get delete => '删除'; String get delete => '删除';
@@ -835,6 +838,9 @@ class SZhTw extends SZh {
@override @override
String get decode => '解碼'; String get decode => '解碼';
@override
String get decompress => '解壓縮';
@override @override
String get delete => '刪除'; String get delete => '刪除';

View File

@@ -5,7 +5,6 @@ class SftpBrowserStatus {
List<SftpName>? files; List<SftpName>? files;
AbsolutePath? path; AbsolutePath? path;
SftpClient? client; SftpClient? client;
bool isBusy = false;
SftpBrowserStatus(); SftpBrowserStatus();
} }

View File

@@ -2,8 +2,8 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; static const String name = "ServerBox";
static const int build = 498; static const int build = 499;
static const String engine = "3.13.0"; static const String engine = "3.13.0";
static const String buildAt = "2023-08-23 21:54:10.937955"; static const String buildAt = "2023-08-25 18:04:51.443337";
static const int modifications = 2; static const int modifications = 4;
} }

View File

@@ -39,6 +39,7 @@
"dark": "Dunkel", "dark": "Dunkel",
"debug": "Debug", "debug": "Debug",
"decode": "Decode", "decode": "Decode",
"decompress": "Dekomprimieren",
"delete": "Löschen", "delete": "Löschen",
"deleteServers": "Batch-Löschung von Servern", "deleteServers": "Batch-Löschung von Servern",
"disabled": "Behinderte", "disabled": "Behinderte",

View File

@@ -39,6 +39,7 @@
"dark": "Dark", "dark": "Dark",
"debug": "Debug", "debug": "Debug",
"decode": "Decode", "decode": "Decode",
"decompress": "Decompress",
"delete": "Delete", "delete": "Delete",
"deleteServers": "Batch delete servers", "deleteServers": "Batch delete servers",
"disabled": "Disabled", "disabled": "Disabled",

View File

@@ -39,6 +39,7 @@
"dark": "Gelap", "dark": "Gelap",
"debug": "Debug", "debug": "Debug",
"decode": "Membaca sandi", "decode": "Membaca sandi",
"decompress": "Dekompresi",
"delete": "Menghapus", "delete": "Menghapus",
"deleteServers": "Penghapusan server secara batch", "deleteServers": "Penghapusan server secara batch",
"disabled": "Dengan disabilitas", "disabled": "Dengan disabilitas",

View File

@@ -39,6 +39,7 @@
"dark": "暗", "dark": "暗",
"debug": "调试", "debug": "调试",
"decode": "解码", "decode": "解码",
"decompress": "解压缩",
"delete": "删除", "delete": "删除",
"deleteServers": "批量删除服务器", "deleteServers": "批量删除服务器",
"disabled": "已禁用", "disabled": "已禁用",

View File

@@ -39,6 +39,7 @@
"dark": "暗", "dark": "暗",
"debug": "調試", "debug": "調試",
"decode": "解碼", "decode": "解碼",
"decompress": "解壓縮",
"delete": "刪除", "delete": "刪除",
"deleteServers": "批量刪除服務器", "deleteServers": "批量刪除服務器",
"disabled": "已禁用", "disabled": "已禁用",

View File

@@ -15,6 +15,7 @@ import '../../../core/extension/numx.dart';
import '../../../core/extension/stringx.dart'; import '../../../core/extension/stringx.dart';
import '../../../core/route.dart'; import '../../../core/route.dart';
import '../../../core/utils/misc.dart'; import '../../../core/utils/misc.dart';
import '../../../core/utils/platform.dart' hide pathJoin;
import '../../../core/utils/ui.dart'; import '../../../core/utils/ui.dart';
import '../../../data/model/server/server_private_info.dart'; import '../../../data/model/server/server_private_info.dart';
import '../../../data/model/sftp/absolute_path.dart'; import '../../../data/model/sftp/absolute_path.dart';
@@ -130,6 +131,7 @@ class _SftpPageState extends State<SftpPage> {
_buildGotoBtn(), _buildGotoBtn(),
_buildUploadBtn(), _buildUploadBtn(),
]; ];
if (isDesktop) children.add(_buildRefreshBtn());
return SafeArea( return SafeArea(
child: Container( child: Container(
padding: const EdgeInsets.fromLTRB(11, 7, 11, 11), padding: const EdgeInsets.fromLTRB(11, 7, 11, 11),
@@ -256,11 +258,14 @@ class _SftpPageState extends State<SftpPage> {
); );
} }
Widget _buildFileView() { Widget _buildRefreshBtn() {
if (_status.isBusy) { return IconButton(
return centerLoading; onPressed: () => _listDir(),
} icon: const Icon(Icons.refresh),
);
}
Widget _buildFileView() {
if (_status.files == null) { if (_status.files == null) {
final p_ = widget.initPath ?? '/'; final p_ = widget.initPath ?? '/';
_status.path = AbsolutePath(p_); _status.path = AbsolutePath(p_);
@@ -341,6 +346,11 @@ class _SftpPageState extends State<SftpPage> {
title: Text(_s.download), title: Text(_s.download),
onTap: () => _download(context, file), onTap: () => _download(context, file),
), ),
ListTile(
leading: const Icon(Icons.folder_zip),
title: Text(_s.decompress),
onTap: () => _decompress(context, file),
),
]); ]);
} }
showRoundDialog( showRoundDialog(
@@ -596,6 +606,30 @@ class _SftpPageState extends State<SftpPage> {
); );
} }
Future<void> _decompress(BuildContext context, SftpName name) async {
context.pop();
final absPath = _getRemotePath(name);
final cmd = _getDecompressCmd(absPath);
if (cmd == null) {
showRoundDialog(
context: context,
title: Text(_s.error),
child: Text('Unsupport file: ${name.filename}'),
actions: [
TextButton(
onPressed: () => context.pop(),
child: Text(_s.ok),
),
],
);
return;
}
showLoadingDialog(context);
await _client?.run(cmd);
context.pop();
_listDir();
}
String _getRemotePath(SftpName name) { String _getRemotePath(SftpName name) {
final prePath = _status.path!.path; final prePath = _status.path!.path;
return pathJoin(prePath, name.filename); return pathJoin(prePath, name.filename);
@@ -607,10 +641,7 @@ class _SftpPageState extends State<SftpPage> {
/// Only return true if the path is changed /// Only return true if the path is changed
Future<bool> _listDir({String? path, SSHClient? client}) async { Future<bool> _listDir({String? path, SSHClient? client}) async {
if (_status.isBusy) { showLoadingDialog(context);
return false;
}
_status.isBusy = true;
if (client != null) { if (client != null) {
final sftpc = await client.sftp(); final sftpc = await client.sftp();
_status.client = sftpc; _status.client = sftpc;
@@ -637,12 +668,13 @@ class _SftpPageState extends State<SftpPage> {
if (mounted) { if (mounted) {
setState(() { setState(() {
_status.files = fs; _status.files = fs;
_status.isBusy = false;
}); });
context.pop();
return true; return true;
} }
return false; return false;
} catch (e, trace) { } catch (e, trace) {
context.pop();
_logger.warning('list dir failed', e, trace); _logger.warning('list dir failed', e, trace);
await _backward(); await _backward();
Future.delayed( Future.delayed(
@@ -669,3 +701,60 @@ class _SftpPageState extends State<SftpPage> {
} }
} }
} }
String? _getDecompressCmd(String filename) {
for (final ext in _extCmdMap.keys) {
if (filename.endsWith('.$ext')) {
return _extCmdMap[ext]?.replaceAll('FILE', '"$filename"');
}
}
return null;
}
/// Translate from
/// https://github.com/ohmyzsh/ohmyzsh/blob/03a0d5bbaedc732436b5c67b166cde954817cc2f/plugins/extract/extract.plugin.zsh
const _extCmdMap = {
'tar.gz': 'tar zxvf FILE',
'tgz': 'tar zxvf FILE',
'tar.bz2': 'tar jxvf FILE',
'tbz2': 'tar jxvf FILE',
'tar.xz': 'tar --xz -xvf FILE',
'txz': 'tar --xz -xvf FILE',
'tar.lzma': 'tar --lzma -xvf FILE',
'tlz': 'tar --lzma -xvf FILE',
'tar.zst': 'tar --zstd -xvf FILE',
'tzst': 'tar --zstd -xvf FILE',
'tar': 'tar xvf FILE',
'tar.lz': 'tar xvf FILE',
'tar.lz4': 'lz4 -c -d FILE | tar xvf - ',
'gz': 'gunzip FILE',
'bz2': 'bunzip2 FILE',
'xz': 'unxz FILE',
'lzma': 'unlzma FILE',
'z': 'uncompress FILE',
'zip': 'unzip FILE',
'war': 'unzip FILE',
'jar': 'unzip FILE',
'ear': 'unzip FILE',
'sublime-package': 'unzip FILE',
'ipa': 'unzip FILE',
'ipsw': 'unzip FILE',
'apk': 'unzip FILE',
'xpi': 'unzip FILE',
'aar': 'unzip FILE',
'whl': 'unzip FILE',
'rar': 'unrar x -ad FILE',
'rpm': 'rpm2cpio FILE | cpio --quiet -id',
'7z': '7za x FILE',
'deb': 'mkdir -p "control" "data"'
'ar vx FILE > /dev/null'
'cd control; extract ../control.tar.*'
'cd ../data; extract ../data.tar.*'
'cd ..; rm *.tar.* debian-binary',
'zst': 'unzstd FILE',
'cab': 'cabextract FILE',
'exe': 'cabextract FILE',
'cpio': 'cpio -idmvF FILE',
'obscpio': 'cpio -idmvF FILE',
'zpaq': 'zpaq x FILE',
};

View File

@@ -474,9 +474,9 @@
baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */; baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 493; CURRENT_PROJECT_VERSION = 499;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.493; MARKETING_VERSION = 1.0.499;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -489,9 +489,9 @@
baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */; baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 493; CURRENT_PROJECT_VERSION = 499;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.493; MARKETING_VERSION = 1.0.499;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -504,9 +504,9 @@
baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */; baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 493; CURRENT_PROJECT_VERSION = 499;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.493; MARKETING_VERSION = 1.0.499;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;