media_service.dart 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import 'dart:io';
  2. import 'package:sino_med_cloud/core/utils/logger.dart';
  3. import 'picker_service.dart';
  4. import 'image_processor.dart';
  5. import 'media_exception.dart';
  6. /// 统一媒体服务入口
  7. ///
  8. /// 提供快捷的图片选择、拍照和处理功能
  9. ///
  10. /// 使用示例:
  11. /// ```dart
  12. /// // 从相册选择并自动压缩
  13. /// final file = await MediaService.pickFromGallery();
  14. ///
  15. /// // 使用系统相机拍照并自动压缩
  16. /// final photo = await MediaService.pickFromCamera();
  17. ///
  18. /// // 使用专业相机拍照(需要手动管理生命周期)
  19. /// final cameraService = CameraService();
  20. /// await cameraService.init();
  21. /// final photo = await cameraService.takePicture();
  22. /// final compressed = await MediaService.compressImage(photo!);
  23. /// cameraService.dispose();
  24. /// ```
  25. class MediaService {
  26. /// 使用系统相机拍照(快捷方式)
  27. ///
  28. /// 自动压缩图片,适合快速拍照场景
  29. ///
  30. /// - [imageQuality] 图片质量(1-100),默认 90
  31. /// - [maxWidth] 压缩后的最大宽度,默认 1080px
  32. /// - [maxHeight] 压缩后的最大高度,默认 1920px
  33. /// - [compressQuality] 压缩质量(1-100),默认 85
  34. ///
  35. /// 返回压缩后的照片文件,如果用户取消或失败返回 null
  36. ///
  37. /// 抛出 [MediaException] 如果操作失败
  38. static Future<File?> pickFromCamera({
  39. int imageQuality = PickerService.defaultImageQuality,
  40. int maxWidth = ImageProcessor.defaultMaxWidth,
  41. int? maxHeight,
  42. int compressQuality = ImageProcessor.defaultQuality,
  43. }) async {
  44. try {
  45. AppLogger.d('开始使用系统相机拍照...');
  46. final file = await PickerService.pickFromCamera(
  47. imageQuality: imageQuality,
  48. );
  49. if (file == null) {
  50. return null;
  51. }
  52. AppLogger.d('开始压缩图片...');
  53. final compressed = await ImageProcessor.compress(
  54. file,
  55. maxWidth: maxWidth,
  56. maxHeight: maxHeight,
  57. quality: compressQuality,
  58. );
  59. AppLogger.d('拍照并压缩完成: ${compressed.path}');
  60. return compressed;
  61. } on MediaException {
  62. rethrow;
  63. } catch (e) {
  64. AppLogger.e('拍照并压缩失败', e);
  65. throw MediaException(
  66. '拍照并压缩失败: ${e.toString()}',
  67. originalError: e,
  68. );
  69. }
  70. }
  71. /// 从相册选择图片(快捷方式)
  72. ///
  73. /// 自动压缩图片,适合快速选择场景
  74. ///
  75. /// - [imageQuality] 图片质量(1-100),默认 90
  76. /// - [maxWidth] 压缩后的最大宽度,默认 1080px
  77. /// - [maxHeight] 压缩后的最大高度,默认 1920px
  78. /// - [compressQuality] 压缩质量(1-100),默认 85
  79. ///
  80. /// 返回压缩后的图片文件,如果用户取消或失败返回 null
  81. ///
  82. /// 抛出 [MediaException] 如果操作失败
  83. static Future<File?> pickFromGallery({
  84. int imageQuality = PickerService.defaultImageQuality,
  85. int maxWidth = ImageProcessor.defaultMaxWidth,
  86. int? maxHeight,
  87. int compressQuality = ImageProcessor.defaultQuality,
  88. }) async {
  89. try {
  90. AppLogger.d('开始从相册选择图片...');
  91. final file = await PickerService.pickFromGallery(
  92. imageQuality: imageQuality,
  93. );
  94. if (file == null) {
  95. return null;
  96. }
  97. AppLogger.d('开始压缩图片...');
  98. final compressed = await ImageProcessor.compress(
  99. file,
  100. maxWidth: maxWidth,
  101. maxHeight: maxHeight,
  102. quality: compressQuality,
  103. );
  104. AppLogger.d('选择并压缩完成: ${compressed.path}');
  105. return compressed;
  106. } on MediaException {
  107. rethrow;
  108. } catch (e) {
  109. AppLogger.e('选择并压缩失败', e);
  110. throw MediaException(
  111. '选择并压缩失败: ${e.toString()}',
  112. originalError: e,
  113. );
  114. }
  115. }
  116. /// 压缩图片
  117. ///
  118. /// - [file] 原始图片文件
  119. /// - [maxWidth] 最大宽度,默认 1080px
  120. /// - [maxHeight] 最大高度,默认 1920px
  121. /// - [quality] 压缩质量(1-100),默认 85
  122. ///
  123. /// 返回压缩后的文件(覆盖原文件)
  124. ///
  125. /// 抛出 [ImageProcessingException] 如果压缩失败
  126. static Future<File> compressImage(
  127. File file, {
  128. int maxWidth = ImageProcessor.defaultMaxWidth,
  129. int? maxHeight,
  130. int quality = ImageProcessor.defaultQuality,
  131. }) async {
  132. return await ImageProcessor.compress(
  133. file,
  134. maxWidth: maxWidth,
  135. maxHeight: maxHeight,
  136. quality: quality,
  137. );
  138. }
  139. /// 获取图片信息
  140. ///
  141. /// 返回图片的宽度、高度和文件大小
  142. ///
  143. /// 抛出 [ImageProcessingException] 如果获取失败
  144. static Future<Map<String, dynamic>> getImageInfo(File file) async {
  145. return await ImageProcessor.getImageInfo(file);
  146. }
  147. /// 医疗脱敏处理(去除 EXIF 信息)
  148. ///
  149. /// 移除图片中的 EXIF 元数据,保护患者隐私
  150. ///
  151. /// 返回脱敏后的文件(覆盖原文件)
  152. ///
  153. /// 抛出 [ImageProcessingException] 如果处理失败
  154. static Future<File> removeExif(File file) async {
  155. return await ImageProcessor.removeExif(file);
  156. }
  157. /// 从相册选择视频
  158. ///
  159. /// 返回选择的视频文件,如果用户取消选择返回 null
  160. ///
  161. /// 抛出 [MediaException] 如果选择失败
  162. static Future<File?> pickVideoFromGallery() async {
  163. return await PickerService.pickVideoFromGallery();
  164. }
  165. }