import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter_common/upload_image/ossUtil.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; // import 'package:images_picker/images_picker.dart'; // import 'package:images_picker/images_picker.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:wechat_assets_picker/wechat_assets_picker.dart'; class _CompressedImageFile { const _CompressedImageFile({ required this.path, required this.fileType, }); final String path; final String fileType; } class _ImageCompressionTarget { const _ImageCompressionTarget({ required this.format, required this.extension, required this.fileType, }); final CompressFormat format; final String extension; final String fileType; } class UploadImagesTool { static String _assetPathNameBuilder(AssetPathEntity path) { final locale = Get.locale ?? WidgetsBinding.instance.platformDispatcher.locale; if (locale.languageCode.toLowerCase() != 'zh') { return path.name; } const systemPathNameMap = { 'Recent': '最近项目', 'Camera': '相机', 'Screenshots': '截屏', 'Downloads': '下载', }; return systemPathNameMap[path.name] ?? path.name; } static uploadImagesTool({ String? oSSAccessKeyId, String? policy, String? callback, String? signature, String? ossDirectory, String? ossHost, required BuildContext context, Function? chooseImagesTap, int? max, bool? isVideo, Widget? isAddOtherWidget, bool? isShowLoading, }) async { await chooseCamera( context: context, max: max, oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', isVideo: isVideo, isAddOtherWidget: isAddOtherWidget, isShowLoading: isShowLoading, chooseImages: (list) => chooseImagesTap?.call(list), ); } /// 动态申请权限,需要区分android和ios,很多时候它两配置权限时各自的名称不同 /// 此处以保存图片需要的配置为例 static Future requestPermission(context) async { late PermissionStatus status; // 1、读取系统权限的弹框 if (Platform.isIOS) { status = await Permission.photosAddOnly.request(); } else { status = await Permission.camera.request(); } // 2、假如你点not allow后,下次点击不会在出现系统权限的弹框(系统权限的弹框只会出现一次), // 这时候需要你自己写一个弹框,然后去打开app权限的页面 if (status != PermissionStatus.granted) { showCupertinoDialog( context: context, builder: (BuildContext ctx) { return CupertinoAlertDialog( title: const Text('您需要去打开权限'), content: const Text('请打开您的相机或者相册权限'), actions: [ CupertinoDialogAction( child: const Text('取消'), onPressed: () { Navigator.pop(ctx); }, ), CupertinoDialogAction( child: const Text('确定'), onPressed: () { openAppSettings(); Navigator.pop(ctx); // 打开手机上该app权限的页面 }, ), ], ); }); } else { return true; } return false; } /// static Future chooseCamera({ required BuildContext context, int? max, String? oSSAccessKeyId, String? policy, String? callback, String? signature, String? ossDirectory, String? ossHost, Function? chooseImages, bool? isVideo, Widget? isAddOtherWidget, bool? isShowLoading, }) async { // showCupertinoModalPopup( context: context, builder: (BuildContext ctx) { return isVideo == true ? CupertinoActionSheet( title: const Text('上传视频'), message: Text('请选择视频'), actions: [ CupertinoActionSheetAction( child: const Text('视频库'), onPressed: () { openGallery( max: max, isVideo: isVideo, oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', chooseImages: (list) => chooseImages?.call(list), ); Get.back(); }, ), ], cancelButton: CupertinoActionSheetAction( isDefaultAction: true, child: const Text('取消'), onPressed: () { Get.back(); }, ), ) : CupertinoActionSheet( title: const Text('上传图片'), message: (max == null || max == 0) ? null : Text('请选择上传方式\n相册最多${max}张'), actions: isAddOtherWidget != null ? [ isAddOtherWidget, CupertinoActionSheetAction( child: const Text('拍照上传'), onPressed: () { openCamera( oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', chooseImages: (list) => chooseImages?.call(list), ); Get.back(); }, ), CupertinoActionSheetAction( child: const Text('相册'), onPressed: () { openGallery( max: max, oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', chooseImages: (list) => chooseImages?.call(list), ); Get.back(); }, ), ] : [ CupertinoActionSheetAction( child: const Text('拍照上传'), onPressed: () { openCamera( oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', chooseImages: (list) => chooseImages?.call(list), ); Get.back(); }, ), CupertinoActionSheetAction( child: const Text('相册'), onPressed: () { openGallery( max: max, oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', isShowLoading: isShowLoading, chooseImages: (list) => chooseImages?.call(list), ); Get.back(); }, ), ], cancelButton: CupertinoActionSheetAction( isDefaultAction: true, child: const Text('取消'), onPressed: () { Get.back(); }, ), ); }); } // static openCamera({ Function? chooseImages, String? oSSAccessKeyId, String? policy, String? callback, String? signature, String? ossDirectory, String? ossHost, }) async { XFile? file = await ImagePicker().pickImage( source: ImageSource.camera, maxHeight: 1080, maxWidth: 1920, imageQuality: 80); if (file == null) { Get.back(); } else { String imgPath = await saveNetworkImg( file, oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', ); chooseImages?.call([imgPath]); } } static openGallery({ Function? chooseImages, String? oSSAccessKeyId, String? policy, String? callback, String? signature, String? ossDirectory, String? ossHost, int? max, bool? isVideo, bool? isShowLoading, }) async { if (isVideo == true) { final List? result = await AssetPicker.pickAssets( Get.context!, pickerConfig: AssetPickerConfig( maxAssets: 1, requestType: RequestType.video, pathNameBuilder: _assetPathNameBuilder, ), ); final File? video = await result?.first.file; String path = await saveNetworkImgGallery( video?.path ?? '', fileType: 'mp4', oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', ); chooseImages?.call([path]); } else { /// 创建临时目录 final Directory tempDir = Directory.systemTemp.createTempSync('compressed_images_'); final List? result = await AssetPicker.pickAssets( Get.context!, pickerConfig: AssetPickerConfig( maxAssets: max ?? 50, pathNameBuilder: _assetPathNameBuilder, ), ); /// 临时存储选中的图片 final List<_CompressedImageFile> selectedFiles = []; if (result != null && result.isNotEmpty) { for (int i = 0; i < result.length; i++) { final File? file = await result[i].file; if (file != null) { final _CompressedImageFile? compressedFile = await _compressGalleryImage(file, tempDir); if (compressedFile != null) { selectedFiles.add(compressedFile); } } } } /// 上传选中的图片 List list = []; for (var element in selectedFiles) { String path = await saveNetworkImgGallery( element.path, fileType: element.fileType, oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', isShowLoading: isShowLoading, ); list.add(path); } chooseImages?.call(list); } } static Future<_CompressedImageFile?> _compressGalleryImage( File file, Directory tempDir, ) async { final _ImageCompressionTarget target = _compressionTargetForPath(file.absolute.path); final XFile? compressedFile = await _compressImageWithTarget( file, tempDir, target, ); if (compressedFile != null) { return _CompressedImageFile( path: compressedFile.path, fileType: target.fileType, ); } if (target.format == CompressFormat.jpeg) { return null; } const _ImageCompressionTarget fallbackTarget = _ImageCompressionTarget( format: CompressFormat.jpeg, extension: 'jpg', fileType: 'jpg', ); final XFile? fallbackFile = await _compressImageWithTarget( file, tempDir, fallbackTarget, ); if (fallbackFile == null) { return null; } return _CompressedImageFile( path: fallbackFile.path, fileType: fallbackTarget.fileType, ); } static Future _compressImageWithTarget( File file, Directory tempDir, _ImageCompressionTarget target, ) async { try { return await FlutterImageCompress.compressAndGetFile( file.absolute.path, '${tempDir.path}/${DateTime.now().microsecondsSinceEpoch}_compressed.${target.extension}', quality: 80, minWidth: 1920, minHeight: 1080, format: target.format, ); } catch (_) { return null; } } static _ImageCompressionTarget _compressionTargetForPath(String path) { final String extension = path.split('?').first.split('.').last.toLowerCase(); switch (extension) { case 'png': return const _ImageCompressionTarget( format: CompressFormat.png, extension: 'png', fileType: 'png', ); case 'webp': return const _ImageCompressionTarget( format: CompressFormat.webp, extension: 'webp', fileType: 'webp', ); case 'heic': case 'heif': return const _ImageCompressionTarget( format: CompressFormat.heic, extension: 'heic', fileType: 'heic', ); case 'jpeg': case 'jpg': default: return const _ImageCompressionTarget( format: CompressFormat.jpeg, extension: 'jpg', fileType: 'jpg', ); } } // 保存网络图片 static Future saveNetworkImg( XFile file, { String? oSSAccessKeyId, String? policy, String? callback, String? signature, String? ossDirectory, String? ossHost, }) async { // print("file.path ===== ${file.path}"); String string = await UploadOss.upload( file.path, fileType: "jpg", oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', ); // print("Gallery ===string== $string"); return string; } // 保存网络图片 static Future saveNetworkImgGallery( String path, { String? fileType, String? oSSAccessKeyId, String? policy, String? callback, String? signature, String? ossDirectory, String? ossHost, bool? isShowLoading, }) async { String string = await UploadOss.upload( path, fileType: fileType ?? "jpg", oSSAccessKeyId: oSSAccessKeyId ?? '', ossHost: ossHost ?? '', ossDirectory: ossDirectory ?? '', policy: policy ?? '', callback: callback ?? '', signature: signature ?? '', isShowLoading: isShowLoading, ); return string; } }