Files
flutter_common/lib/upload_image/ossUtil.dart
2025-10-29 18:13:33 +08:00

278 lines
9.1 KiB
Dart
Executable File
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: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,
bool? isShowLoading,
}) 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",
),
});
if (isShowLoading == true) {
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);
}
}
}