Files
flutter_common/lib/upload_image/upload_images_tool.dart
wuxinglong 179ce76a53 feat(upload_image): 支持微信资源选择及图片压缩上传
- 集成 wechat_assets_picker 库实现图片和视频选择
- 使用 flutter_image_compress 库对选中图片进行压缩处理
- 替换原有 ImagePicker,实现多图片和视频的资源选取功能
- 压缩后图片保存为临时文件再进行上传
- 优化上传过程,兼容视频和图片的不同处理流程
- 简化回调调用代码,提升代码可读性与一致性
2026-02-06 13:46:55 +08:00

374 lines
13 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:io';
import 'dart:math';
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 UploadImagesTool {
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<bool> 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: <Widget>[
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<void> 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: <Widget>[
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 ?? 9}'),
actions: isAddOtherWidget != null
? <Widget>[
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();
},
),
]
: <Widget>[
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,
);
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<AssetEntity>? result = await AssetPicker.pickAssets(
Get.context!,
pickerConfig: AssetPickerConfig(maxAssets: 1, requestType: RequestType.video),
);
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 List<AssetEntity>? result = await AssetPicker.pickAssets(
Get.context!,
pickerConfig: AssetPickerConfig(maxAssets: max ?? 50),
);
/// 临时存储选中的图片
final List<XFile> 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 String extension = file.absolute.path.split('.').last;
// 压缩并保存到临时文件
final XFile? compressedFile = await FlutterImageCompress.compressAndGetFile(
file.absolute.path, '${file.parent.path}/${DateTime.now().millisecondsSinceEpoch}_compressed.$extension',
quality: 80, minWidth: 1920, minHeight: 1080);
if (compressedFile != null) {
selectedFiles.add(compressedFile);
}
}
}
}
/// 上传选中的图片
List<String> list = [];
for (var element in selectedFiles) {
String path = await saveNetworkImgGallery(
element.path,
oSSAccessKeyId: oSSAccessKeyId ?? '',
ossHost: ossHost ?? '',
ossDirectory: ossDirectory ?? '',
policy: policy ?? '',
callback: callback ?? '',
signature: signature ?? '',
isShowLoading: isShowLoading,
);
list.add(path);
}
chooseImages?.call(list);
}
}
// 保存网络图片
static Future<String> 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<String> 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;
}
}