init
This commit is contained in:
		
							
								
								
									
										26
									
								
								lib/data_provider/data_provider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								lib/data_provider/data_provider.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
class Item {
 | 
			
		||||
  bool isFolder;
 | 
			
		||||
  Uri uri;
 | 
			
		||||
  String name;
 | 
			
		||||
 | 
			
		||||
  Item(this.isFolder, this.uri, this.name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Folder {
 | 
			
		||||
  List<Item> items;
 | 
			
		||||
  Uri self;
 | 
			
		||||
  Uri parent;
 | 
			
		||||
 | 
			
		||||
  Folder(this.items, this.self, this.parent);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
abstract class DataProvider {
 | 
			
		||||
  final List<String> validSuffix = [".jpg", ".jpeg", ".png"];
 | 
			
		||||
 | 
			
		||||
  void connect();
 | 
			
		||||
  Future<Folder> listOfFiles({Uri? uri});
 | 
			
		||||
  ImageProvider getImageProvider(Uri uri);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								lib/data_provider/local_data_provider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								lib/data_provider/local_data_provider.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/cupertino.dart';
 | 
			
		||||
import 'package:gallery/data_provider/data_provider.dart';
 | 
			
		||||
 | 
			
		||||
class LocalDataProvider extends DataProvider {
 | 
			
		||||
  final String initialPath;
 | 
			
		||||
 | 
			
		||||
  LocalDataProvider(this.initialPath);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  void connect() {}
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<Folder> listOfFiles({Uri? uri}) async {
 | 
			
		||||
    final dir = uri != null ? Directory.fromUri(uri) : Directory(initialPath);
 | 
			
		||||
 | 
			
		||||
    final list = dir.listSync();
 | 
			
		||||
    final List<Item> res = [];
 | 
			
		||||
 | 
			
		||||
    for (var val in list) {
 | 
			
		||||
      if (validSuffix.any((suff) => val.path.endsWith(suff))) {
 | 
			
		||||
        res.add(Item(false, val.uri, val.uri.pathSegments.last));
 | 
			
		||||
      } else if (await FileSystemEntity.isDirectory(val.path)) {
 | 
			
		||||
        res.add(
 | 
			
		||||
            Item(true, val.uri, val.uri.pathSegments.reversed.skip(1).first));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    res.sort(
 | 
			
		||||
      (a, b) => b.isFolder ? 1 : -1,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return Folder(res, dir.uri, dir.parent.uri);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ImageProvider getImageProvider(Uri uri) {
 | 
			
		||||
    return FileImage(File.fromUri(uri));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										148
									
								
								lib/data_provider/ssh_data_provider.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								lib/data_provider/ssh_data_provider.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,148 @@
 | 
			
		||||
import 'dart:io';
 | 
			
		||||
import 'dart:typed_data';
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
class SSHDataProvider extends DataProvider {
 | 
			
		||||
  final String host;
 | 
			
		||||
  final int port;
 | 
			
		||||
  final String username;
 | 
			
		||||
  final String password;
 | 
			
		||||
  final 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(host, port),
 | 
			
		||||
      username: username,
 | 
			
		||||
      onPasswordRequest: () => password,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    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(initialPath);
 | 
			
		||||
 | 
			
		||||
    final items = await sftpClient!.listdir(dir.path);
 | 
			
		||||
    List<Item> res = [];
 | 
			
		||||
    for (final val in items) {
 | 
			
		||||
      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));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Folder(res, dir.uri, dir.parent.uri);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ImageProvider getImageProvider(Uri uri) {
 | 
			
		||||
    return _SSHImageProvider(uri, sftpClient!);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _SSHImageProvider extends ImageProvider<_SSHImageProvider> {
 | 
			
		||||
  /// Creates an object that decodes a [File] as an image.
 | 
			
		||||
  ///
 | 
			
		||||
  /// The arguments must not be null.
 | 
			
		||||
  const _SSHImageProvider(this.uri, this.sftpClient, {this.scale = 1.0})
 | 
			
		||||
      : assert(scale != null);
 | 
			
		||||
 | 
			
		||||
  /// The file to decode into an image.
 | 
			
		||||
  final Uri uri;
 | 
			
		||||
  final SftpClient sftpClient;
 | 
			
		||||
 | 
			
		||||
  /// The scale to place in the [ImageInfo] object of the image.
 | 
			
		||||
  final double scale;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<_SSHImageProvider> obtainKey(ImageConfiguration configuration) {
 | 
			
		||||
    return SynchronousFuture<_SSHImageProvider>(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ImageStreamCompleter load(_SSHImageProvider key, DecoderCallback decode) {
 | 
			
		||||
    return MultiFrameImageStreamCompleter(
 | 
			
		||||
      codec: _loadAsync(key, null, decode),
 | 
			
		||||
      scale: key.scale,
 | 
			
		||||
      debugLabel: key.uri.path,
 | 
			
		||||
      informationCollector: () => <DiagnosticsNode>[
 | 
			
		||||
        ErrorDescription('Path: ${uri.path}'),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  ImageStreamCompleter loadBuffer(
 | 
			
		||||
      _SSHImageProvider key, DecoderBufferCallback decode) {
 | 
			
		||||
    return MultiFrameImageStreamCompleter(
 | 
			
		||||
      codec: _loadAsync(key, decode, null),
 | 
			
		||||
      scale: key.scale,
 | 
			
		||||
      debugLabel: key.uri.path,
 | 
			
		||||
      informationCollector: () => <DiagnosticsNode>[
 | 
			
		||||
        ErrorDescription('Path: ${uri.path}'),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<ui.Codec> _loadAsync(_SSHImageProvider key,
 | 
			
		||||
      DecoderBufferCallback? decode, DecoderCallback? decodeDeprecated) async {
 | 
			
		||||
    assert(key == this);
 | 
			
		||||
 | 
			
		||||
    final file = await sftpClient.open(uri.toFilePath());
 | 
			
		||||
    // todo do not load whole image in ram, create tempfile instead.
 | 
			
		||||
    final Uint8List bytes = await file.readBytes();
 | 
			
		||||
 | 
			
		||||
    if (bytes.lengthInBytes == 0) {
 | 
			
		||||
      // The file may become available later.
 | 
			
		||||
      PaintingBinding.instance.imageCache.evict(key);
 | 
			
		||||
      throw StateError('$file is empty and cannot be loaded as an image.');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (decode != null) {
 | 
			
		||||
      return decode(await ui.ImmutableBuffer.fromUint8List(bytes));
 | 
			
		||||
    }
 | 
			
		||||
    return decodeDeprecated!(bytes);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  bool operator ==(Object other) {
 | 
			
		||||
    if (other.runtimeType != runtimeType) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    return other is _SSHImageProvider &&
 | 
			
		||||
        other.uri.path == uri.path &&
 | 
			
		||||
        other.scale == scale;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  int get hashCode => Object.hash(uri.path, scale);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String toString() =>
 | 
			
		||||
      '${objectRuntimeType(this, 'FileImage')}("${uri.path}", scale: $scale)';
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user