Gallery/lib/data_provider/ssh_data_provider.dart

146 lines
3.9 KiB
Dart

import 'dart:io';
import 'package:dartssh2/dartssh2.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:gallery/data_provider/data_provider.dart';
import 'dart:ui' as ui show Codec, ImmutableBuffer;
import 'package:path_provider/path_provider.dart';
class SSHDataProvider extends DataProvider {
final Future<String> host;
final Future<int> port;
final Future<String> username;
final Future<String> password;
final Future<String> initialPath;
SftpClient? sftpClient;
SSHClient? sshClient;
SSHDataProvider(
{required this.host,
required this.port,
required this.initialPath,
required this.password,
required this.username});
@override
Future<void> connect() async {
if (sshClient != null && !sshClient!.isClosed) return;
sshClient = SSHClient(
await SSHSocket.connect(await host, await port),
username: await username,
onPasswordRequest: () => password,
);
await sshClient?.authenticated;
sftpClient = await sshClient?.sftp();
}
@override
Future<Folder> listOfFiles({Uri? uri}) async {
await connect();
if (sftpClient == null) throw const FormatException("");
final dir =
uri != null ? Directory.fromUri(uri) : Directory(await initialPath);
final items = await sftpClient!.listdir(dir.path);
List<Item> res = [];
for (final val in items) {
if (val.filename == "." || val.filename == "..") continue;
if (validSuffix.any((suff) => val.filename.endsWith(suff))) {
res.add(Item(false, Uri.file(dir.path + val.filename), val.filename));
} else if (val.attr.isDirectory) {
res.add(
Item(true, Uri.directory(dir.path + val.filename), val.filename));
}
}
res.sort(
(a, b) => b.isFolder ? 1 : -1,
);
return Folder(res, dir.uri, dir.parent.uri);
}
@override
ImageProvider getImageProvider(Uri uri) {
return _SSHImageProvider(uri, sftpClient!);
}
}
class _SSHImageProvider extends ImageProvider<_SSHImageProvider> {
const _SSHImageProvider(this.uri, this.sftpClient);
final Uri uri;
final SftpClient sftpClient;
@override
Future<_SSHImageProvider> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<_SSHImageProvider>(this);
}
@override
ImageStreamCompleter loadBuffer(
_SSHImageProvider key, DecoderBufferCallback decode) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
scale: 1.0,
debugLabel: key.uri.path,
informationCollector: () => <DiagnosticsNode>[
ErrorDescription('Path: ${uri.path}'),
],
);
}
Future<ui.Codec> _loadAsync(
_SSHImageProvider key, DecoderBufferCallback decode) async {
assert(key == this);
Directory tempDir = await getTemporaryDirectory();
String tempPath = "${tempDir.path}/gallery";
// check if temp file exists
Uint8List bytes;
final File tmpPic = File(tempPath + uri.toFilePath());
if (await tmpPic.exists()) {
// use temp file
bytes = await tmpPic.readAsBytes();
} else {
final file = await sftpClient.open(uri.toFilePath());
bytes = await file.readBytes();
() async {
await tmpPic.create(recursive: true);
await tmpPic.writeAsBytes(bytes);
}();
}
if (bytes.lengthInBytes == 0) {
// The file may become available later.
PaintingBinding.instance.imageCache.evict(key);
throw StateError(
'bytes are empty is empty and cannot be loaded as an image.');
}
return decode(await ui.ImmutableBuffer.fromUint8List(bytes));
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is _SSHImageProvider && other.uri.path == uri.path;
}
@override
int get hashCode => uri.path.hashCode;
@override
String toString() => '${objectRuntimeType(this, 'FileImage')}("${uri.path}")';
}