file_storage_native_and_web.dart 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import 'dart:io';
  2. import 'dart:typed_data';
  3. import 'package:fis_common/logger/logger.dart';
  4. import 'package:path_provider/path_provider.dart';
  5. import 'package:fis_common/env/env.dart';
  6. import 'package:idb_shim/idb.dart';
  7. import 'package:idb_shim/idb_browser.dart';
  8. /// 文件存储
  9. abstract class FileStorageNativeAndWeb {
  10. FileStorageNativeAndWeb({
  11. required this.directory,
  12. });
  13. /// 存储目录
  14. final String directory;
  15. Future<Uint8List?> read(String fileName) async {
  16. try {
  17. return await _getFile(fileName);
  18. } catch (e) {
  19. logger.e("File storage fetch fail, platform: ${FPlatform.current}.", e);
  20. return null;
  21. }
  22. }
  23. Future<bool> save(String fileName, Uint8List file) async {
  24. try {
  25. return await _setFile(fileName, file);
  26. } catch (e) {
  27. logger.e("Text storage save fail, platform: ${FPlatform.current}.", e);
  28. return false;
  29. }
  30. }
  31. Future<bool> delete(String fileName) async {
  32. try {
  33. return await _deleteFile(fileName);
  34. } catch (e) {
  35. logger.e("Text storage detail fail, platform: ${FPlatform.current}.", e);
  36. return false;
  37. }
  38. }
  39. Future<bool> deleteDirectory(String directoryPath) async {
  40. final storagePath = await _getStoragePath();
  41. final directory = Directory(storagePath);
  42. final result = await directory.exists();
  43. if (result) {
  44. try {
  45. await directory.delete(recursive: true);
  46. return true;
  47. } catch (e) {
  48. print(e);
  49. return false;
  50. }
  51. }
  52. return false;
  53. }
  54. Future<void> _checkAndCreateDir(String dir) async {
  55. final directory = Directory(dir);
  56. final parentDirectory = directory.parent;
  57. try {
  58. if (!await parentDirectory.exists()) {
  59. await parentDirectory.create(recursive: true);
  60. }
  61. } catch (e) {
  62. print(e);
  63. }
  64. if (!await directory.exists()) {
  65. try {
  66. await directory.create();
  67. } catch (e) {
  68. print(e);
  69. }
  70. }
  71. }
  72. Future<String> _getStoragePath() async {
  73. Directory? dir;
  74. if (Platform.isAndroid) {
  75. dir = await getExternalStorageDirectory();
  76. }
  77. dir ??= await getApplicationDocumentsDirectory();
  78. if (directory.isNotEmpty) {
  79. return "${dir.path}/$directory";
  80. }
  81. return dir.path;
  82. }
  83. Future<bool> _setFile(String fileName, Uint8List file);
  84. Future<Uint8List?> _getFile(String fileName);
  85. Future<bool> _deleteFile(String fileName);
  86. /// 创建实例
  87. ///
  88. /// [storageName] 文件名
  89. static FileStorageNativeAndWeb createInstance(String storageName) {
  90. final platform = FPlatform.current;
  91. switch (platform) {
  92. case FPlatformEnum.android:
  93. case FPlatformEnum.iOS:
  94. return _FileStorageNative(storageName);
  95. case FPlatformEnum.webOnMac:
  96. case FPlatformEnum.webOnWin:
  97. return _FileStorageShellWeb(storageName);
  98. case FPlatformEnum.web:
  99. return _FileStoragePureWeb(storageName);
  100. }
  101. }
  102. }
  103. ///本地化的文件存储
  104. class _FileStorageNative extends FileStorageNativeAndWeb {
  105. _FileStorageNative(String directory) : super(directory: directory);
  106. @override
  107. Future<Uint8List?> _getFile(String fileName) async {
  108. final storagePath = await _getStoragePath();
  109. final filePath = "$storagePath/$fileName";
  110. final file = File(filePath);
  111. if (!await file.exists()) return null;
  112. final text = await file.readAsBytes();
  113. return text;
  114. }
  115. @override
  116. Future<bool> _setFile(String fileName, Uint8List file) async {
  117. final storagePath = await _getStoragePath();
  118. final filePath = "$storagePath/$fileName";
  119. File fileToSave = File(filePath);
  120. _checkAndCreateDir(storagePath);
  121. if (!await fileToSave.exists()) {
  122. try {
  123. fileToSave = await fileToSave.create(recursive: true);
  124. } catch (e) {
  125. print(e);
  126. }
  127. }
  128. await fileToSave.writeAsBytes(file.toList());
  129. return true;
  130. }
  131. @override
  132. Future<String> _getStoragePath() async {
  133. Directory? dir;
  134. if (Platform.isAndroid) {
  135. dir = await getExternalStorageDirectory();
  136. }
  137. dir ??= await getApplicationDocumentsDirectory();
  138. return "${dir.path}/$directory";
  139. }
  140. @override
  141. Future<bool> _deleteFile(String fileName) async {
  142. final storagePath = await _getStoragePath();
  143. final filePath = "$storagePath/$fileName";
  144. final file = File(filePath);
  145. if (await file.exists()) {
  146. try {
  147. await file.delete();
  148. return true;
  149. } catch (e) {
  150. print(e);
  151. return false;
  152. }
  153. }
  154. return false;
  155. }
  156. }
  157. ///壳子下的文件存储
  158. class _FileStorageShellWeb extends FileStorageNativeAndWeb {
  159. _FileStorageShellWeb(String directory) : super(directory: directory);
  160. @override
  161. Future<Uint8List?> _getFile(String fileName) async {
  162. return null;
  163. }
  164. @override
  165. Future<bool> _setFile(String fileName, Uint8List file) async {
  166. return true;
  167. }
  168. @override
  169. Future<bool> _deleteFile(String fileName) async {
  170. return true;
  171. }
  172. }
  173. ///纯web端的文件缓存
  174. class _FileStoragePureWeb extends FileStorageNativeAndWeb {
  175. _FileStoragePureWeb(String directory) : super(directory: directory);
  176. static const int _version = 1;
  177. static const String _dbName = 'files.db';
  178. static const String _objectStoreName = 'files';
  179. static const String _propNameFilePath = 'filePath';
  180. static const String _propNameFileContents = 'contents';
  181. Future<Database> _openDb() async {
  182. final idbFactory = getIdbFactory();
  183. if (idbFactory == null) {
  184. throw Exception('getIdbFactory() failed');
  185. }
  186. return idbFactory.open(
  187. _dbName,
  188. version: _version,
  189. onUpgradeNeeded: (e) => e.database
  190. .createObjectStore(_objectStoreName, keyPath: _propNameFilePath),
  191. );
  192. }
  193. Future<bool> exists() async {
  194. final db = await _openDb();
  195. final txn = db.transaction(_objectStoreName, idbModeReadOnly);
  196. final store = txn.objectStore(_objectStoreName);
  197. final object = await store.getObject(directory);
  198. await txn.completed;
  199. return object != null;
  200. }
  201. Future<Uint8List> readAsBytes() async {
  202. final db = await _openDb();
  203. final txn = db.transaction(_objectStoreName, idbModeReadOnly);
  204. final store = txn.objectStore(_objectStoreName);
  205. final object = await store.getObject(directory) as Map?;
  206. await txn.completed;
  207. if (object == null) {
  208. throw Exception('file not found: $directory');
  209. }
  210. return object['contents'] as Uint8List;
  211. }
  212. Future<String> readAsString() async {
  213. final db = await _openDb();
  214. final txn = db.transaction(_objectStoreName, idbModeReadOnly);
  215. final store = txn.objectStore(_objectStoreName);
  216. final object = await store.getObject(directory) as Map?;
  217. await txn.completed;
  218. if (object == null) {
  219. writeAsBytes(Uint8List(0));
  220. return "";
  221. }
  222. return object['contents'] as String;
  223. }
  224. Future<void> writeAsBytes(Uint8List contents) async {
  225. final db = await _openDb();
  226. final txn = db.transaction(_objectStoreName, idbModeReadWrite);
  227. final store = txn.objectStore(_objectStoreName);
  228. await store.put({
  229. _propNameFilePath: directory,
  230. _propNameFileContents: contents
  231. }); // if the file exists, it will be replaced.
  232. await txn.completed;
  233. }
  234. Future<void> writeAsString(String contents) async {
  235. final db = await _openDb();
  236. final txn = db.transaction(_objectStoreName, idbModeReadWrite);
  237. final store = txn.objectStore(_objectStoreName);
  238. await store.put({
  239. _propNameFilePath: directory,
  240. _propNameFileContents: contents
  241. }); // if the file exists, it will be replaced.
  242. await txn.completed;
  243. }
  244. @override
  245. Future<Uint8List?> _getFile(String fileName) async {
  246. final uint8List = await readAsBytes();
  247. return uint8List;
  248. }
  249. @override
  250. Future<bool> _setFile(String fileName, Uint8List file) async {
  251. await writeAsBytes(file);
  252. return true;
  253. }
  254. @override
  255. Future<bool> _deleteFile(String fileName) async {
  256. final db = await _openDb();
  257. final txn = db.transaction(_objectStoreName, idbModeReadWrite);
  258. final store = txn.objectStore(_objectStoreName);
  259. await store.delete(directory);
  260. await txn.completed;
  261. return true;
  262. }
  263. }