import 'dart:io'; import 'dart:typed_data'; import 'package:fis_common/logger/logger.dart'; import 'package:path_provider/path_provider.dart'; import 'package:fis_common/env/env.dart'; import 'package:idb_shim/idb.dart'; import 'package:idb_shim/idb_browser.dart'; /// 文件存储 abstract class FileStorageNativeAndWeb { FileStorageNativeAndWeb({ required this.directory, }); /// 存储目录 final String directory; Future read(String fileName) async { try { return await _getFile(fileName); } catch (e) { logger.e("File storage fetch fail, platform: ${FPlatform.current}.", e); return null; } } Future save(String fileName, Uint8List file) async { try { return await _setFile(fileName, file); } catch (e) { logger.e("Text storage save fail, platform: ${FPlatform.current}.", e); return false; } } Future delete(String fileName) async { try { return await _deleteFile(fileName); } catch (e) { logger.e("Text storage detail fail, platform: ${FPlatform.current}.", e); return false; } } Future deleteDirectory(String directoryPath) async { final storagePath = await _getStoragePath(); final directory = Directory(storagePath); final result = await directory.exists(); if (result) { try { await directory.delete(recursive: true); return true; } catch (e) { print(e); return false; } } return false; } Future _checkAndCreateDir(String dir) async { final directory = Directory(dir); final parentDirectory = directory.parent; try { if (!await parentDirectory.exists()) { await parentDirectory.create(recursive: true); } } catch (e) { print(e); } if (!await directory.exists()) { try { await directory.create(); } catch (e) { print(e); } } } Future _getStoragePath() async { Directory? dir; if (Platform.isAndroid) { dir = await getExternalStorageDirectory(); } dir ??= await getApplicationDocumentsDirectory(); if (directory.isNotEmpty) { return "${dir.path}/$directory"; } return dir.path; } Future _setFile(String fileName, Uint8List file); Future _getFile(String fileName); Future _deleteFile(String fileName); /// 创建实例 /// /// [storageName] 文件名 static FileStorageNativeAndWeb createInstance(String storageName) { final platform = FPlatform.current; switch (platform) { case FPlatformEnum.android: case FPlatformEnum.iOS: return _FileStorageNative(storageName); case FPlatformEnum.webOnMac: case FPlatformEnum.webOnWin: return _FileStorageShellWeb(storageName); case FPlatformEnum.web: return _FileStoragePureWeb(storageName); } } } ///本地化的文件存储 class _FileStorageNative extends FileStorageNativeAndWeb { _FileStorageNative(String directory) : super(directory: directory); @override Future _getFile(String fileName) async { final storagePath = await _getStoragePath(); final filePath = "$storagePath/$fileName"; final file = File(filePath); if (!await file.exists()) return null; final text = await file.readAsBytes(); return text; } @override Future _setFile(String fileName, Uint8List file) async { final storagePath = await _getStoragePath(); final filePath = "$storagePath/$fileName"; File fileToSave = File(filePath); _checkAndCreateDir(storagePath); if (!await fileToSave.exists()) { try { fileToSave = await fileToSave.create(recursive: true); } catch (e) { print(e); } } await fileToSave.writeAsBytes(file.toList()); return true; } @override Future _getStoragePath() async { Directory? dir; if (Platform.isAndroid) { dir = await getExternalStorageDirectory(); } dir ??= await getApplicationDocumentsDirectory(); return "${dir.path}/$directory"; } @override Future _deleteFile(String fileName) async { final storagePath = await _getStoragePath(); final filePath = "$storagePath/$fileName"; final file = File(filePath); if (await file.exists()) { try { await file.delete(); return true; } catch (e) { print(e); return false; } } return false; } } ///壳子下的文件存储 class _FileStorageShellWeb extends FileStorageNativeAndWeb { _FileStorageShellWeb(String directory) : super(directory: directory); @override Future _getFile(String fileName) async { return null; } @override Future _setFile(String fileName, Uint8List file) async { return true; } @override Future _deleteFile(String fileName) async { return true; } } ///纯web端的文件缓存 class _FileStoragePureWeb extends FileStorageNativeAndWeb { _FileStoragePureWeb(String directory) : super(directory: directory); static const int _version = 1; static const String _dbName = 'files.db'; static const String _objectStoreName = 'files'; static const String _propNameFilePath = 'filePath'; static const String _propNameFileContents = 'contents'; Future _openDb() async { final idbFactory = getIdbFactory(); if (idbFactory == null) { throw Exception('getIdbFactory() failed'); } return idbFactory.open( _dbName, version: _version, onUpgradeNeeded: (e) => e.database .createObjectStore(_objectStoreName, keyPath: _propNameFilePath), ); } Future exists() async { final db = await _openDb(); final txn = db.transaction(_objectStoreName, idbModeReadOnly); final store = txn.objectStore(_objectStoreName); final object = await store.getObject(directory); await txn.completed; return object != null; } Future readAsBytes() async { final db = await _openDb(); final txn = db.transaction(_objectStoreName, idbModeReadOnly); final store = txn.objectStore(_objectStoreName); final object = await store.getObject(directory) as Map?; await txn.completed; if (object == null) { throw Exception('file not found: $directory'); } return object['contents'] as Uint8List; } Future readAsString() async { final db = await _openDb(); final txn = db.transaction(_objectStoreName, idbModeReadOnly); final store = txn.objectStore(_objectStoreName); final object = await store.getObject(directory) as Map?; await txn.completed; if (object == null) { writeAsBytes(Uint8List(0)); return ""; } return object['contents'] as String; } Future writeAsBytes(Uint8List contents) async { final db = await _openDb(); final txn = db.transaction(_objectStoreName, idbModeReadWrite); final store = txn.objectStore(_objectStoreName); await store.put({ _propNameFilePath: directory, _propNameFileContents: contents }); // if the file exists, it will be replaced. await txn.completed; } Future writeAsString(String contents) async { final db = await _openDb(); final txn = db.transaction(_objectStoreName, idbModeReadWrite); final store = txn.objectStore(_objectStoreName); await store.put({ _propNameFilePath: directory, _propNameFileContents: contents }); // if the file exists, it will be replaced. await txn.completed; } @override Future _getFile(String fileName) async { final uint8List = await readAsBytes(); return uint8List; } @override Future _setFile(String fileName, Uint8List file) async { await writeAsBytes(file); return true; } @override Future _deleteFile(String fileName) async { final db = await _openDb(); final txn = db.transaction(_objectStoreName, idbModeReadWrite); final store = txn.objectStore(_objectStoreName); await store.delete(directory); await txn.completed; return true; } }