feat(delete):删除里面文件夹

This commit is contained in:
2025-10-23 10:47:53 +08:00
parent 35d26643ba
commit 55ab438c11
75 changed files with 2424 additions and 41 deletions

View File

@@ -0,0 +1,142 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter_common/utils/toast_utils.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:image_gallery_saver_plus/image_gallery_saver_plus.dart';
// import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:permission_handler/permission_handler.dart';
class DownLoadImageTool {
// 请求照片库权限
static Future<bool> requestPhotoPermission() async {
// 检查当前权限状态
var status = await Permission.photos.status;
if (status.isDenied) {
// 请求权限
status = await Permission.photos.request();
// 如果用户拒绝了权限,可以显示一个解释
if (status.isPermanentlyDenied) {
// 打开应用设置,让用户手动启用权限
await openAppSettings();
}
if (status.isDenied) {
// 打开应用设置,让用户手动启用权限
await openAppSettings();
}
}
return status.isGranted;
}
// 或者请求存储权限适用于Android
static Future<bool> requestStoragePermission() async {
if (Platform.isAndroid) {
// 对于Android 13及以上版本
if (await DeviceInfoPlugin().androidInfo.then((info) => info.version.sdkInt) >= 33) {
var status = await Permission.photos.request();
if(status == PermissionStatus.denied){
await requestPhotoPermission();
}
return status.isGranted;
} else {
// 对于Android 13以下版本
var status = await Permission.storage.request();
return status.isGranted;
}
} else {
// iOS使用照片权限
return await requestPhotoPermission();
}
}
///保存到相册
static Future<dynamic> savePhoto({
required String imageUrl,
bool? isHideLoading,
}) async {
//获取保存相册权限,如果没有,则申请改权限
bool permition = await requestStoragePermission();
if (permition) {
var result = imageRequest(
imageUrl: imageUrl,
isHideLoading: isHideLoading,
);
return result;
} else {
//重新请求--第一次请求权限时,保存方法不会走,需要重新调一次
ToastUtils.showToast(msg: '请打开手机相册权限');
// savePhoto(imageUrl: imageUrl);
}
}
static Future<dynamic> imageRequest({
required String imageUrl,
bool? isHideLoading,
}) async {
if (isHideLoading == true) {
} else {
await EasyLoading.show(
// status: 'loading...',
maskType: EasyLoadingMaskType.black,
);
}
var response = await Dio().get(
imageUrl,
options: Options(
responseType: ResponseType.bytes,
),
);
if (isHideLoading == true) {
} else {
EasyLoading.dismiss();
}
final result = await ImageGallerySaverPlus.saveImage(
Uint8List.fromList(response.data),
quality: 60,
name: "hello",
isReturnImagePathOfIOS: true,
);
print('=result ============ $result');
return result;
}
///fetchImageAsUint8List
static Future<Uint8List?> fetchImageAsUint8List(String imageUrl) async {
await EasyLoading.show(
// status: 'loading...',
maskType: EasyLoadingMaskType.black,
);
try {
var response = await Dio().get(
imageUrl,
options: Options(
responseType: ResponseType.bytes,
),
);
// final response = await http.get(Uri.parse(imageUrl));
if (response.statusCode == 200) {
EasyLoading.dismiss();
return response.data;
} else {
// debugPrint('Failed to load image: ${response.statusCode}');
EasyLoading.dismiss();
return null;
}
} catch (e) {
EasyLoading.dismiss();
// debugPrint('Error fetching image: $e');
return null;
}
}
}

View File

@@ -0,0 +1,93 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:image_gallery_saver_plus/image_gallery_saver_plus.dart';
// import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadLocalTool {
/// 拿到存储路径
/// 使用getTemporaryDirectory()方法可以获取应用程序的临时目录,该目录用于存储应用程序的临时数据。这个目录在应用程序退出后会被清空
Future<String> getTemporaryDirectoryString() async {
final directory = await getTemporaryDirectory();
return directory.path;
}
/// 使用getApplicationDocumentsDirectory()方法可以获取应用程序的文档目录,该目录用于存储应用程序的私有数据。
Future<String> getApplicationDocumentsDirectoryString() async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
/// 使用getExternalStorageDirectory()方法可以获取设备的外部存储目录,该目录用于存储应用程序的公共数据。需要注意的是,在某些设备上,外部存储目录可能是不可用的。
Future<String> getExternalStorageDirectoryString() async {
final directory = await getExternalStorageDirectory();
return directory?.path ?? "";
}
/// 创建对文件位置的引用
Future<File> localFile(String fileName) async {
final path = await getApplicationDocumentsDirectoryString();
return File('$path/$fileName');
}
///save
Future<dynamic> saveNetworkVideoFile({
required String fileName,
required String fileUrl,
}) async {
final path = await getApplicationDocumentsDirectoryString();
String savePath = "$path/$fileName ";
// String fileUrl =
// "https://s3.cn-north-1.amazonaws.com.cn/mtab.kezaihui.com/video/ForBiggerBlazes.mp4";
//
await Dio().download(fileUrl, savePath, onReceiveProgress: (count, total) {
print("${(count / total * 100).toStringAsFixed(0)}%");
});
final result = await ImageGallerySaverPlus.saveFile(savePath);
return result;
// print(result);
}
///save
Future<dynamic> saveNetworkVideoFileExternalStorageDirectory({
required String fileName,
required String fileUrl,
}) async {
final path = await getApplicationDocumentsDirectoryString();
String savePath = "$path/$fileName ";
// String fileUrl =
// "https://s3.cn-north-1.amazonaws.com.cn/mtab.kezaihui.com/video/ForBiggerBlazes.mp4";
//
await Dio().download(fileUrl, savePath, onReceiveProgress: (count, total) {
print("${(count / total * 100).toStringAsFixed(0)}%");
});
final result = await ImageGallerySaverPlus.saveFile(savePath);
return result;
// print(result);
}
// // 将数据写入文件
// Future<File> writeCounter(int counter) async {
// final file = await _localFile;
// // Write the file
// return file.writeAsString('$counter');
// }
//
// // 从文件中读取数据
// Future<int> readCounter() async {
// try {
// final file = await _localFile;
// // Read the file
// String contents = await file.readAsString();
//
// return int.parse(contents);
// } catch (e) {
// // If we encounter an error, return 0
// return 0;
// }
// }
}

View File

@@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart';
class LookImagesTool {
static lookImages({
required List<String> listData,
int? currentPage,
}) async {
showDialog(
context: Get.context!,
builder: (_) {
return LookImagesWidget(
listData: listData,
currentPage: currentPage,
);
});
}
}
class LookImagesWidget extends StatefulWidget {
final List<String> listData;
final int? currentPage;
const LookImagesWidget({
super.key,
required this.listData,
this.currentPage,
});
@override
State<LookImagesWidget> createState() => _LookImagesWidgetState();
}
class _LookImagesWidgetState extends State<LookImagesWidget> {
List listData = [];
late int currentPage;
late int initialPage = 0;
@override
void initState() {
listData = widget.listData;
if (widget.currentPage == null) {
initialPage = 0;
currentPage = 0;
} else {
// initialPage = 0;
currentPage = widget.currentPage ?? 0;
}
super.initState();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => Get.back(),
child: Stack(
children: [
Scaffold(
backgroundColor: Colors.black,
body: Center(
child: PhotoViewGallery.builder(
itemCount: listData.length,
pageController: PageController(initialPage: currentPage),
onPageChanged: (index) {
setState(() {
currentPage = index;
});
},
builder: (_, index) {
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(
listData[index],
),
);
},
),
),
),
//图片张数指示器
Positioned(
left: 0,
right: 0,
bottom: 20,
child: Container(
alignment: Alignment.center,
child: Text(
"${currentPage + 1}/${listData.length}",
style: const TextStyle(
color: Colors.white,
fontSize: 16,
decoration: TextDecoration.none,
),
),
),
)
],
),
);
}
}

274
lib/upload_image/ossUtil.dart Executable file
View File

@@ -0,0 +1,274 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:ui';
import 'package:crypto/crypto.dart';
import "package:dio/dio.dart";
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_common/utils/toast_utils.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:image_gallery_saver_plus/image_gallery_saver_plus.dart';
// import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class UploadOss {
/*
* @params file 要上传的文件对象
* @params rootDir 阿里云oss设置的根目录文件夹名字
* @param fileType 文件类型例如jpg,mp4等
* @param callback 回调函数我这里用于传cancelToken方便后期关闭请求
* @param onSendProgress 上传的进度事件
*/
static Future<String> upload(
String path, {
String rootDir = "moment",
required String fileType,
required String oSSAccessKeyId,
required String policy,
required String callback,
required String signature,
required String ossDirectory,
required String ossHost,
}) async {
// 生成oss的路径和文件名我这里目前设置的是moment/20201229/test.mp4
String pathName = "$rootDir/${getDate()}/app-${getRandom(12)}.$fileType";
// 请求参数的form对象
FormData formdata = FormData.fromMap({
'OSSAccessKeyId': oSSAccessKeyId,
'policy': policy,
'callback': callback,
'signature': signature,
'key': '$ossDirectory$pathName',
//上传后的文件名
'success_action_status': '200',
'file': MultipartFile.fromFileSync(
path,
contentType: fileType == 'mp4' ? null : DioMediaType("image", "jpg"),
filename: "${getRandom(12)}.$fileType",
),
});
await EasyLoading.show(
// status: 'loading...',
maskType: EasyLoadingMaskType.black,
);
Dio dio = Dio();
dio.options.responseType = ResponseType.plain;
// dio.options.method = 'put';
// dio.options.contentType = "multipart/form-data;image/jpg";
try {
// 发送请求
Response response = await dio.post(
ossHost,
data: formdata,
options: Options(
contentType: "multipart/form-data;image/jpg",
headers: {'Content-Type': 'multipart/form-data;image/jpg'},
),
);
print("response ===== $response");
EasyLoading.dismiss();
// 成功后返回文件访问路径
return "$ossHost/$ossDirectory$pathName";
} on DioError catch (e) {
EasyLoading.dismiss();
print("e.message ===== ${e.message}");
print("e.data ===== ${e.response?.data}");
// print("e.headers ===== ${e.response?.headers}");
// print("e.extra ===== ${e.response?.extra}");
return '';
}
}
/*
* 生成固定长度的随机字符串
* */
static String getRandom(int num) {
String alphabet = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";
String left = "";
for (var i = 0; i < num; i++) {
// right = right + (min + (Random().nextInt(max - min))).toString();
left = left + alphabet[Random().nextInt(alphabet.length)];
}
return left;
}
/// 获取日期
static String getDate() {
DateTime now = DateTime.now();
return "${now.year}${now.month}${now.day}";
}
}
class UploadWidgetOss {
static Future<String> uploadWidgetImage(
GlobalKey globalKey, {
required String oSSAccessKeyId,
required String policy,
required String callback,
required String signature,
required String ossDirectory,
required String ossHost,
}) async {
///通过globalkey将Widget保存为ui.Image
ui.Image _image = await getImageFromWidget(globalKey);
///异步将这张图片保存在手机内部存储目录下
String? localImagePath = await saveImageByUIImage(_image, isEncode: false);
///保存完毕后关闭当前页面并将保存的图片路径返回到上一个页面
String? url = await UploadOss.upload(
localImagePath,
fileType: "png",
oSSAccessKeyId: oSSAccessKeyId,
policy: policy,
callback: callback,
signature: signature,
ossDirectory: ossDirectory,
ossHost: ossHost,
);
return url;
}
// 将一个Widget转为image.Image对象
static Future<ui.Image> getImageFromWidget(GlobalKey globalKey) async {
// globalKey为需要图像化的widget的key
RenderRepaintBoundary? boundary =
globalKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
// 转换为图像
ui.Image img = await boundary!.toImage(pixelRatio: 2);
return img;
}
///将指定的文件保存到目录空间中。
///[image] 这里是使用的ui包下的Image
///[picName] 保存到本地的文件图片文件名如test_image
///[endFormat]保存到本地的文件图片文件格式如png
///[isReplace]当本地存在同名的文件图片true就是替换
///[isEncode]对保存的文件(图片)进行编码
/// 最终保存到本地的文件 (图片)的名称为 picName.endFormat
static Future<String> saveImageByUIImage(ui.Image image,
{String? picName,
String endFormat = "png",
bool isReplace = true,
bool isEncode = true}) async {
///获取本地磁盘路径
/*
* 在Android平台中获取的是/data/user/0/com.studyyoun.flutterbookcode/app_flutter
* 此方法在在iOS平台获取的是Documents路径
*/
Directory appDocDir = await getApplicationDocumentsDirectory();
String appDocPath = appDocDir.path;
///拼接目录
if (picName == null || picName.trim().isEmpty) {
///当用户没有指定picName时取当前的时间命名
picName = "${DateTime.now().millisecond.toString()}.$endFormat";
} else {
picName = "$picName.$endFormat";
}
if (isEncode) {
///对保存的图片名字加密
picName = md5.convert(utf8.encode(picName)).toString();
}
appDocPath = "$appDocPath/$picName";
///校验图片是否存在
var file = File(appDocPath);
bool exist = await file.exists();
if (exist) {
if (isReplace) {
///如果图片存在就进行删除替换
///如果新的图片加载失败,那么旧的图片也被删除了
await file.delete();
} else {
///如果图片存在就不进行下载
return "";
}
}
ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List();
///将Uint8List的数据格式保存
await File(appDocPath).writeAsBytes(pngBytes);
return appDocPath;
}
//申请存本地相册权限
static Future<bool> getPormiation() async {
if (Platform.isIOS) {
var status = await Permission.photos.status;
if (status.isDenied) {
Map<Permission, PermissionStatus> statuses = await [
Permission.photos,
].request();
// saveImage(globalKey);
}
return status.isGranted;
} else {
var status = await Permission.storage.status;
if (status.isDenied) {
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
].request();
}
return status.isGranted;
}
}
///保存到相册
static void savePhoto(GlobalKey globalKey) async {
RenderRepaintBoundary? boundary =
globalKey.currentContext!.findRenderObject() as RenderRepaintBoundary?;
double dpr = ui.window.devicePixelRatio; // 获取当前设备的像素比
var image = await boundary!.toImage(pixelRatio: dpr);
// 将image转化成byte
ByteData? byteData = await image.toByteData(format: ImageByteFormat.png);
//获取保存相册权限,如果没有,则申请改权限
bool permition = await getPormiation();
var status = await Permission.photos.status;
if (permition) {
if (Platform.isIOS) {
if (status.isGranted) {
Uint8List images = byteData!.buffer.asUint8List();
final result = await ImageGallerySaverPlus.saveImage(images,
quality: 60, name: "hello");
// EasyLoading.showToast("保存成功");
ToastUtils.showToast(msg: "保存成功");
}
if (status.isDenied) {
print("IOS拒绝");
}
} else {
//安卓
if (status.isGranted) {
print("Android已授权");
Uint8List images = byteData!.buffer.asUint8List();
final result =
await ImageGallerySaverPlus.saveImage(images, quality: 60);
if (result != null) {
// EasyLoading.showToast("保存成功");
ToastUtils.showToast(msg: "保存成功");
} else {
print('error');
// toast("保存失败");
}
}
}
} else {
//重新请求--第一次请求权限时,保存方法不会走,需要重新调一次
savePhoto(globalKey);
}
}
}

View File

@@ -0,0 +1,375 @@
import 'dart:io';
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_common/utils/customer.dart';
import 'package:flutter_common/utils/toast_utils.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
// import 'package:images_picker/images_picker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'ossUtil.dart';
class UploadImages extends StatefulWidget {
final Function? chooseImages; // List<String>
final int? max; //最大照片数量
final int? hNumber; //一排最大几个
final bool? onlyShow; //仅展示,不操作
final BoxFit? fit;
final List<String>? imagesList; //图片数组
final String oSSAccessKeyId;
final String policy;
final String callback;
final String signature;
final String ossDirectory;
final String ossHost;
final Widget carmaWidget;
final Function? oneTap; //点击的哪一个
final Function? deleteTap; //删除一个后
const UploadImages({
super.key,
this.chooseImages,
this.max = 9,
this.onlyShow = false,
this.imagesList,
required this.oSSAccessKeyId,
required this.policy,
required this.callback,
required this.signature,
required this.ossDirectory,
required this.ossHost,
required this.carmaWidget,
this.hNumber = 3,
this.fit,
this.oneTap,
this.deleteTap,
});
@override
State<UploadImages> createState() => _UploadImagesState();
}
class _UploadImagesState extends State<UploadImages> {
List<String> imagesList = [];
bool isMax = false; //是否达到最大值
@override
void initState() {
// print("widget.maxwidget.max ============ ${widget.max}");
if (widget.imagesList != null) {
imagesList = widget.imagesList ?? [];
}
super.initState();
}
///数组的数量
setImageListLength() {
// print("widget.maxwidget.max ============ ${widget.max}");
// print("imagesList.length ============ ${imagesList.length}");
if (imagesList.length < (widget.max?.toInt() ?? 9)) {
isMax = false;
return imagesList.length + 1;
} else if (imagesList.length == (widget.max?.toInt() ?? 9)) {
isMax = true;
return imagesList.length;
} else {
isMax = true;
return 9;
}
}
@override
Widget build(BuildContext context) {
return MasonryGridView.count(
padding: const EdgeInsets.only(top: 0),
crossAxisCount: 3,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: setImageListLength(),
itemBuilder: (_, int index) {
if (isMax == true) {
String? imageUrl = imagesList[index];
return cellDeleteWidget(
index: index,
child: GestureDetector(
onTap: () => widget.oneTap?.call(index),
child: imageNetworkWidget(
imageUrl: imageUrl,
index: index,
),
),
);
} else {
if (index == setImageListLength() - 1) {
return widget.onlyShow == true
? const SizedBox()
: uploadWidget(context);
} else {
String? imageUrl = imagesList[index];
return cellDeleteWidget(
index: index,
child: imageNetworkWidget(
imageUrl: imageUrl,
index: index,
),
);
}
}
},
mainAxisSpacing: 15,
crossAxisSpacing: 8,
);
}
Widget imageNetworkWidget({
required String imageUrl,
required int index,
}) {
return GestureDetector(
onTap: () => widget.oneTap?.call(index),
child: CustomerImagesNetworking(
imageUrl: imageUrl,
fit: widget.fit ?? BoxFit.cover,
),
);
}
///cell 总样式
Widget cellWidget({required Widget child}) {
return SizedBox(
key: Key(cellKeyString()),
width: (Get.width - 32 - 32) / (widget.hNumber ?? 3),
height: (Get.width - 32 - 32) / (widget.hNumber ?? 3),
child: child,
);
}
///cell key 随机数+时间
String cellKeyString({String? string}) {
var random = Random();
int randomNumber = random.nextInt(10000); // 生成0到10000000000000之间的随机整数
return '$randomNumber + $string + ${DateTime.now().toString()}';
}
///有删除样式的cell
Widget cellDeleteWidget({
required Widget child,
required int index,
}) {
return Stack(
alignment: Alignment.topRight,
children: [
cellWidget(child: child),
widget.onlyShow == true
? const SizedBox()
: GestureDetector(
onTap: () {
imagesList.removeAt(index);
widget.deleteTap?.call(imagesList);
setState(() {});
},
child: ClipOval(
child: Container(
width: 30.w,
height: 30.w,
color: Colors.grey.withOpacity(0.5),
child: Center(
child: Icon(
Icons.close,
size: 25.w,
color: Colors.white,
),
),
),
),
),
],
);
}
///上传的Widget
Widget uploadWidget(BuildContext context) {
return GestureDetector(
onTap: () async {
if (Platform.isIOS) {
await chooseCamera(
context: context,
max: widget.max,
);
} else {
await chooseCamera(
context: context,
max: widget.max,
);
// bool b = await requestPermission(context);
// if (b == true) {
// await chooseCamera(
// context: context,
// max: widget.max,
// );
// }
}
},
child: cellWidget(
child: widget.carmaWidget,
),
);
}
/// 动态申请权限需要区分android和ios很多时候它两配置权限时各自的名称不同
/// 此处以保存图片需要的配置为例
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: () {
Navigator.pop(ctx);
// 打开手机上该app权限的页面
openAppSettings();
},
),
],
);
});
} else {
return true;
}
return false;
}
///
Future<void> chooseCamera({
required BuildContext context,
int? max,
}) async {
//
showCupertinoModalPopup(
context: context,
builder: (BuildContext ctx) {
return CupertinoActionSheet(
title: const Text('上传图片'),
message: Text('请选择上传方式\n相册最多${max ?? 9}'),
actions: <Widget>[
CupertinoActionSheetAction(
child: const Text('拍照上传'),
onPressed: () {
openCamera();
Get.back();
},
),
CupertinoActionSheetAction(
child: const Text('相册'),
onPressed: () {
openGallery();
Get.back();
},
),
],
cancelButton: CupertinoActionSheetAction(
isDefaultAction: true,
child: const Text('取消'),
onPressed: () {
Get.back();
},
),
);
});
}
//
openCamera() async {
XFile? file = await ImagePicker().pickImage(
source: ImageSource.camera,
);
if (file == null) {
// Get.back();
} else {
String imgPath = await saveNetworkImg(
file,
);
imagesList.add(imgPath);
widget.chooseImages?.call(imagesList);
setState(() {});
}
}
openGallery() async {
int number = (widget.max ?? 9) - imagesList.length;
// List<Media>? images =
// await ImagesPicker.pick(count: number, pickType: PickType.image);
List<String> list = [];
List<XFile>? images = await ImagePicker().pickMultiImage(limit: number,);
if (images.isEmpty != true) {
for (var element in images) {
String path = await saveNetworkImgGallery(
element.path,
);
list.add(path);
}
imagesList.addAll(list);
widget.chooseImages?.call(imagesList);
setState(() {});
} else {
ToastUtils.showToast(msg: "请选择图片");
}
}
// 保存网络图片
Future<String> saveNetworkImg(XFile file) async {
// print("file.path ===== ${file.path}");
String string = await UploadOss.upload(
file.path,
fileType: "jpg",
oSSAccessKeyId: widget.oSSAccessKeyId,
ossHost: widget.ossHost,
ossDirectory: widget.ossDirectory,
policy: widget.policy,
callback: widget.callback,
signature: widget.signature,
);
return string;
}
// 保存网络图片
Future<String> saveNetworkImgGallery(String path) async {
String string = await UploadOss.upload(
path,
fileType: "jpg",
oSSAccessKeyId: widget.oSSAccessKeyId,
ossHost: widget.ossHost,
ossDirectory: widget.ossDirectory,
policy: widget.policy,
callback: widget.callback,
signature: widget.signature,
);
return string;
}
}

View File

@@ -0,0 +1,341 @@
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter_common/upload_image/ossUtil.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';
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,
}) async {
await chooseCamera(
context: context,
max: max ?? 9,
oSSAccessKeyId: oSSAccessKeyId ?? '',
ossHost: ossHost ?? '',
ossDirectory: ossDirectory ?? '',
policy: policy ?? '',
callback: callback ?? '',
signature: signature ?? '',
isVideo: isVideo,
isAddOtherWidget: isAddOtherWidget,
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,
}) 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: 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 ?? '',
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,
}) async {
if (isVideo == true) {
XFile? video = await ImagePicker().pickVideo(source: ImageSource.gallery);
String path = await saveNetworkImgGallery(
video?.path ?? '',
fileType: 'mp4',
oSSAccessKeyId: oSSAccessKeyId ?? '',
ossHost: ossHost ?? '',
ossDirectory: ossDirectory ?? '',
policy: policy ?? '',
callback: callback ?? '',
signature: signature ?? '',
);
chooseImages?.call([path]);
print('video path ============ $path');
} else {
List<XFile>? images = await ImagePicker().pickMultiImage();
List<String> list = [];
for (var element in images) {
String path = await saveNetworkImgGallery(
element.path,
oSSAccessKeyId: oSSAccessKeyId ?? '',
ossHost: ossHost ?? '',
ossDirectory: ossDirectory ?? '',
policy: policy ?? '',
callback: callback ?? '',
signature: signature ?? '',
);
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,
}) async {
String string = await UploadOss.upload(
path,
fileType: fileType ?? "jpg",
oSSAccessKeyId: oSSAccessKeyId ?? '',
ossHost: ossHost ?? '',
ossDirectory: ossDirectory ?? '',
policy: policy ?? '',
callback: callback ?? '',
signature: signature ?? '',
);
return string;
}
}