media_service.dart 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. /// - [compressQuality] 压缩质量(1-100),默认 85
  33. ///
  34. /// 返回压缩后的照片文件,如果用户取消或失败返回 null
  35. ///
  36. /// 抛出 [MediaException] 如果操作失败
  37. static Future<File?> pickFromCamera({
  38. int imageQuality = PickerService.defaultImageQuality,
  39. int maxWidth = ImageProcessor.defaultMaxWidth,
  40. int compressQuality = ImageProcessor.defaultQuality,
  41. }) async {
  42. try {
  43. AppLogger.d('开始使用系统相机拍照...');
  44. final file = await PickerService.pickFromCamera(
  45. imageQuality: imageQuality,
  46. );
  47. if (file == null) {
  48. return null;
  49. }
  50. AppLogger.d('开始压缩图片...');
  51. final compressed = await ImageProcessor.compress(
  52. file,
  53. maxWidth: maxWidth,
  54. quality: compressQuality,
  55. );
  56. AppLogger.d('拍照并压缩完成: ${compressed.path}');
  57. return compressed;
  58. } on MediaException {
  59. rethrow;
  60. } catch (e) {
  61. AppLogger.e('拍照并压缩失败', e);
  62. throw MediaException(
  63. '拍照并压缩失败: ${e.toString()}',
  64. originalError: e,
  65. );
  66. }
  67. }
  68. /// 从相册选择图片(快捷方式)
  69. ///
  70. /// 自动压缩图片,适合快速选择场景
  71. ///
  72. /// - [imageQuality] 图片质量(1-100),默认 90
  73. /// - [maxWidth] 压缩后的最大宽度,默认 1080px
  74. /// - [compressQuality] 压缩质量(1-100),默认 85
  75. ///
  76. /// 返回压缩后的图片文件,如果用户取消或失败返回 null
  77. ///
  78. /// 抛出 [MediaException] 如果操作失败
  79. static Future<File?> pickFromGallery({
  80. int imageQuality = PickerService.defaultImageQuality,
  81. int maxWidth = ImageProcessor.defaultMaxWidth,
  82. int compressQuality = ImageProcessor.defaultQuality,
  83. }) async {
  84. try {
  85. AppLogger.d('开始从相册选择图片...');
  86. final file = await PickerService.pickFromGallery(
  87. imageQuality: imageQuality,
  88. );
  89. if (file == null) {
  90. return null;
  91. }
  92. AppLogger.d('开始压缩图片...');
  93. final compressed = await ImageProcessor.compress(
  94. file,
  95. maxWidth: maxWidth,
  96. quality: compressQuality,
  97. );
  98. AppLogger.d('选择并压缩完成: ${compressed.path}');
  99. return compressed;
  100. } on MediaException {
  101. rethrow;
  102. } catch (e) {
  103. AppLogger.e('选择并压缩失败', e);
  104. throw MediaException(
  105. '选择并压缩失败: ${e.toString()}',
  106. originalError: e,
  107. );
  108. }
  109. }
  110. /// 压缩图片
  111. ///
  112. /// - [file] 原始图片文件
  113. /// - [maxWidth] 最大宽度,默认 1080px
  114. /// - [quality] 压缩质量(1-100),默认 85
  115. ///
  116. /// 返回压缩后的文件(覆盖原文件)
  117. ///
  118. /// 抛出 [ImageProcessingException] 如果压缩失败
  119. static Future<File> compressImage(
  120. File file, {
  121. int maxWidth = ImageProcessor.defaultMaxWidth,
  122. int quality = ImageProcessor.defaultQuality,
  123. }) async {
  124. return await ImageProcessor.compress(
  125. file,
  126. maxWidth: maxWidth,
  127. quality: quality,
  128. );
  129. }
  130. /// 获取图片信息
  131. ///
  132. /// 返回图片的宽度、高度和文件大小
  133. ///
  134. /// 抛出 [ImageProcessingException] 如果获取失败
  135. static Future<Map<String, dynamic>> getImageInfo(File file) async {
  136. return await ImageProcessor.getImageInfo(file);
  137. }
  138. /// 医疗脱敏处理(去除 EXIF 信息)
  139. ///
  140. /// 移除图片中的 EXIF 元数据,保护患者隐私
  141. ///
  142. /// 返回脱敏后的文件(覆盖原文件)
  143. ///
  144. /// 抛出 [ImageProcessingException] 如果处理失败
  145. static Future<File> removeExif(File file) async {
  146. return await ImageProcessor.removeExif(file);
  147. }
  148. /// 从相册选择视频
  149. ///
  150. /// 返回选择的视频文件,如果用户取消选择返回 null
  151. ///
  152. /// 抛出 [MediaException] 如果选择失败
  153. static Future<File?> pickVideoFromGallery() async {
  154. return await PickerService.pickVideoFromGallery();
  155. }
  156. }