#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'**
String get decode;
/// No description provided for @decompress.
///
/// In en, this message translates to:
/// **'Decompress'**
String get decompress;
/// No description provided for @delete.
///
/// In en, this message translates to:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,8 +2,8 @@
class BuildData {
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 buildAt = "2023-08-23 21:54:10.937955";
static const int modifications = 2;
static const String buildAt = "2023-08-25 18:04:51.443337";
static const int modifications = 4;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,6 +15,7 @@ import '../../../core/extension/numx.dart';
import '../../../core/extension/stringx.dart';
import '../../../core/route.dart';
import '../../../core/utils/misc.dart';
import '../../../core/utils/platform.dart' hide pathJoin;
import '../../../core/utils/ui.dart';
import '../../../data/model/server/server_private_info.dart';
import '../../../data/model/sftp/absolute_path.dart';
@@ -130,6 +131,7 @@ class _SftpPageState extends State<SftpPage> {
_buildGotoBtn(),
_buildUploadBtn(),
];
if (isDesktop) children.add(_buildRefreshBtn());
return SafeArea(
child: Container(
padding: const EdgeInsets.fromLTRB(11, 7, 11, 11),
@@ -256,11 +258,14 @@ class _SftpPageState extends State<SftpPage> {
);
}
Widget _buildFileView() {
if (_status.isBusy) {
return centerLoading;
}
Widget _buildRefreshBtn() {
return IconButton(
onPressed: () => _listDir(),
icon: const Icon(Icons.refresh),
);
}
Widget _buildFileView() {
if (_status.files == null) {
final p_ = widget.initPath ?? '/';
_status.path = AbsolutePath(p_);
@@ -341,6 +346,11 @@ class _SftpPageState extends State<SftpPage> {
title: Text(_s.download),
onTap: () => _download(context, file),
),
ListTile(
leading: const Icon(Icons.folder_zip),
title: Text(_s.decompress),
onTap: () => _decompress(context, file),
),
]);
}
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) {
final prePath = _status.path!.path;
return pathJoin(prePath, name.filename);
@@ -607,10 +641,7 @@ class _SftpPageState extends State<SftpPage> {
/// Only return true if the path is changed
Future<bool> _listDir({String? path, SSHClient? client}) async {
if (_status.isBusy) {
return false;
}
_status.isBusy = true;
showLoadingDialog(context);
if (client != null) {
final sftpc = await client.sftp();
_status.client = sftpc;
@@ -637,12 +668,13 @@ class _SftpPageState extends State<SftpPage> {
if (mounted) {
setState(() {
_status.files = fs;
_status.isBusy = false;
});
context.pop();
return true;
}
return false;
} catch (e, trace) {
context.pop();
_logger.warning('list dir failed', e, trace);
await _backward();
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 */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 493;
CURRENT_PROJECT_VERSION = 499;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.493;
MARKETING_VERSION = 1.0.499;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -489,9 +489,9 @@
baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 493;
CURRENT_PROJECT_VERSION = 499;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.493;
MARKETING_VERSION = 1.0.499;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -504,9 +504,9 @@
baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 493;
CURRENT_PROJECT_VERSION = 499;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.493;
MARKETING_VERSION = 1.0.499;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;