flutter实现选择图片视频上传到oss和图片视频的预览功能

news/2024/10/18 20:19:54/

一、效果图

flutter实现选择图片视频上传到oss和图片视频的预览功

二、所需要的依赖

image_picker: ^1.1.0 //选择图片
flutter_oss_aliyun: ^6.4.1 //图片上传到阿里云oss
uuid: ^4.4.0 //生成唯一uuid
interactiveviewer_gallery: ^0.6.0 //图片视频预览
cached_network_image: ^3.3.1 //缓存网络图片,避免多次请求
video_thumbnail: ^0.5.3 //视频生成缩略图
video_player: ^2.8.6 //视频播放

三、主要源码解析

1、初始化oss,参考 flutter_oss_aliyun库

initUploadOss() {Auth authGetter() {return Auth(accessKey: "",accessSecret: '',expire: '2024-05-23T13:26:58Z',secureToken:'',);}Client.init(ossEndpoint: ProjectConfig.ossEndpoint,bucketName: ProjectConfig.bucketName,authGetter: authGetter);
}

2、选择上传图片视频方式

List<Map<String, dynamic>> selectPictureMethodDicData = [{"label": "选择照片","value": "0",},{"label": "拍照","value": "1",},{"label": "选择视频","value": "2",},{"label": "拍视频","value": "3",},{"label": "选择单张照片/视频","value": "4",},{"label": "选择多张照片或者视频","value": "5",},
];

3、定义MedaiModel类

import 'dart:typed_data';class MedaiModel {int process;String ossUrl;final String localUrl;final Uint8List bytes;final String extension;final String id;final String thumbnailPath;final String sourceType;MedaiModel({required this.process,required this.ossUrl,required this.localUrl,required this.bytes,required this.extension,required this.id,required this.thumbnailPath,required this.sourceType,});factory MedaiModel.fromJson(Map<String, dynamic> json) {return MedaiModel(process: json["process"] ?? 0,ossUrl: json["ossUrl"] ?? "",localUrl: json["localUrl"] ?? "",bytes: json["bytes"] ?? Uint8List(0),extension: json["extension"] ?? "",id: json["id"] ?? "",thumbnailPath: json["thumbnailPath"] ?? "",sourceType: json["sourceType"] ?? "",);}Map<String, dynamic> toJson() {return {'process': process,'ossUrl': ossUrl,'localUrl': localUrl,'bytes': bytes,'extension': extension,'id': id,'thumbnailPath': thumbnailPath,'sourceType': sourceType,};}
}

4、根据选择视频方式产品图片视频资源

Future selectMedia(String value, RxList<dynamic>? filesController,{bool isMultiple = false,int fileSize = 0,int limit = 1,int imageQuality = 90,Duration? maxDuration,bool isMedia = false}) async {final ImagePicker picker = ImagePicker();ImageSource imageSource = ImageSource.gallery;List<XFile>? files = [];if (value == "0" || value == "2") {//相册imageSource = ImageSource.gallery;} else if (value == "1" || value == "3") {//相机imageSource = ImageSource.camera;}if (value == "0" || value == "1") {//照片if (isMultiple) {final List<XFile> images =await picker.pickMultiImage(imageQuality: imageQuality, limit: limit);files.addAll(images);} else {final XFile? image = await picker.pickImage(source: imageSource, imageQuality: imageQuality);if (image != null) {files.add(image);}}} else if (value == "2" || value == "3") {//视频final XFile? image =await picker.pickVideo(source: imageSource, maxDuration: maxDuration);if (image != null) {files.add(image);}}if (value == "4") {final XFile? media = await picker.pickMedia(imageQuality: imageQuality);if (media != null) {files.add(media);}} else if (value == "5") {final List<XFile> medias = await picker.pickMultipleMedia(imageQuality: imageQuality, limit: limit);if (medias.length > 1) {files.addAll(medias);}}for (var file in files) {bool isUpload = true;Uint8List? bytes = await file.readAsBytes();if (fileSize > 0) {int? byte = bytes.lengthInBytes;if (byte / 1024 / 1024 > fileSize) {EasyLoading.showToast("资源大小应小于${fileSize}M");isUpload = false;}}if (isUpload) {int indexOf = file.path.lastIndexOf(".");String extension = file.path.substring(indexOf + 1);String sourceType = getSourceType(file.path);String thumbnailPath = file.path;if (sourceType == 'video') {thumbnailPath = await getVideoThumbnail(file.path);}MedaiModel medaiModel = MedaiModel.fromJson({"uploadProcess": 0,"ossUrl": "","localUrl": file.path,"bytes": bytes,"extension": extension,"id": Uuid().v1(),"thumbnailPath": thumbnailPath,"sourceType": sourceType,});filesController!.add(medaiModel);}}for (int i = 0; i < filesController!.length; i++) {MedaiModel file = filesController[i];if (file.ossUrl == "") {String uploadPath ='public/upload-test/${getNowDate()}/${Uuid().v1()}.${file.extension}';try {await Client().putObject(file.bytes,uploadPath,option: PutRequestOption(onSendProgress: (int sent, int total) {file.process = ((sent / total) * 100).toInt();print("上传的进度${file.process}");filesController.refresh(); //不添加此句,会在下一次才更新},),);file.ossUrl ="https://${ProjectConfig.bucketName}.${ProjectConfig.ossEndpoint}/$uploadPath";} catch (e) {print("上传失败$e");}}}
}

5、判断资源类型

String getSourceType(String path) {String ossPath = path.split("?")[0];int index = ossPath.lastIndexOf(".");String typeStr = ossPath.substring(index + 1).toUpperCase();List<String> imagesList = ["BMP", "JPG", "JPEG", "PNG", "GIF"];List<String> videoList = ["AVI", "WMV", "MPG", "MPEG", "MOV", "MP4"];if (imagesList.contains(typeStr)) {return "image";} else if (videoList.contains(typeStr)) {return "video";} else {return "image";}
}

6、获取视频缩略图

Future<String> getVideoThumbnail(String path) async {String? thumbnailPath = await VideoThumbnail.thumbnailFile(video: path, imageFormat: ImageFormat.JPEG, maxWidth: 128, quality: 25);return thumbnailPath!;
}

7、获取资源显示的视图

Widget getSourceView(MedaiModel source,{double width = 100, double height = 100}) {if (source.sourceType == 'video') {return Image.file(File(source.thumbnailPath),width: width, height: height, fit: BoxFit.cover);} else {if (source.localUrl != '') {return Image.file(File(source.localUrl),width: width, height: height, fit: BoxFit.cover);} else {return CachedNetworkImage(imageUrl: source.ossUrl,width: width,height: height,fit: BoxFit.cover);}}
}

8、打开预览图片

void openGallery(List<MedaiModel> sourceList, MedaiModel source) {int initIndex = 0;for (int i = 0; i < sourceList.length; i++) {if (sourceList[i].id == source.id) {initIndex = i;break;}}Navigator.of(Get.context!).push(HeroDialogRoute<void>(builder: (BuildContext context) => DisplayGesture(child: InteractiveviewerGallery<dynamic>(sources: sourceList,initIndex: initIndex,itemBuilder: (BuildContext context, int index, bool isFocus) {MedaiModel sourceEntity = sourceList[index];if (sourceEntity.sourceType == 'video') {return PreviewVideo(sourceEntity,isFocus: isFocus,);} else {return PreviewImage(sourceEntity);}},onPageChanged: (int pageIndex) {print("nell-pageIndex:$pageIndex");},),),),);
}

9、预览图片视图

import 'dart:io';import 'package:cached_network_image/cached_network_image.dart';
import 'package:company_manage_flutter/model/mediaModel.dart';
import 'package:flutter/material.dart';class PreviewImage extends StatefulWidget {MedaiModel  source;PreviewImage(this.source);@overrideState<PreviewImage> createState() => _PreviewImageState();
}class _PreviewImageState extends State<PreviewImage> {@overrideWidget build(BuildContext context) {return GestureDetector(behavior: HitTestBehavior.opaque,onTap: () => Navigator.of(context).pop(),child: Center(child: Hero(tag: widget.source.id,child: widget.source.localUrl == ''? CachedNetworkImage(imageUrl: widget.source.ossUrl,fit: BoxFit.contain,): Image.file(File(widget.source.localUrl),fit: BoxFit.contain,),),),);}
}

10、预览视频视图

import 'dart:io';import 'package:company_manage_flutter/model/mediaModel.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';class PreviewVideo extends StatefulWidget {final MedaiModel source;final bool? isFocus;PreviewVideo(this.source, {this.isFocus});@overrideState<PreviewVideo> createState() => _PreviewVideoState();
}class _PreviewVideoState extends State<PreviewVideo> {VideoPlayerController? _controller;late VoidCallback listener;_PreviewVideoState() {listener = () {if (!mounted) {return;}setState(() {});};}@overridevoid initState() {super.initState();init();}init() async {if (widget.source.localUrl == '') {_controller =VideoPlayerController.networkUrl(Uri.parse(widget.source.ossUrl));} else {_controller =VideoPlayerController.file(File(widget.source.localUrl));}// loop play//  _controller!.setLooping(true);await _controller!.initialize();setState(() {});_controller!.addListener(listener);}@overridevoid dispose() {super.dispose();_controller!.removeListener(listener);_controller?.pause();_controller?.dispose();}@overridevoid didUpdateWidget(covariant PreviewVideo oldWidget) {super.didUpdateWidget(oldWidget);if (oldWidget.isFocus! && !widget.isFocus!) {// pause_controller?.pause();}}@overrideWidget build(BuildContext context) {return _controller!.value.isInitialized? Stack(alignment: Alignment.center,children: [GestureDetector(onTap: () {setState(() {_controller!.value.isPlaying? _controller!.pause(): _controller!.play();});},child: Hero(tag: widget.source.id,child: AspectRatio(aspectRatio: _controller!.value.aspectRatio,child: VideoPlayer(_controller!),),),),_controller!.value.isPlaying == true? SizedBox(): IgnorePointer(ignoring: true,child: Icon(Icons.play_arrow,size: 100,color: Colors.red,),),],): Theme(data: ThemeData(cupertinoOverrideTheme:CupertinoThemeData(brightness: Brightness.dark)),child: CupertinoActivityIndicator(radius: 30));}
}

http://www.ppmy.cn/news/1446887.html

相关文章

(python)动态规划

前言 曾经有一位叫做小明的年轻人,他生活在一个被困在连绵不断的山脉中的村庄里。这座村庄每年都会受到洪水的威胁,而村民们只能通过一条崎岖而危险的小路逃离洪水的侵袭。小明决定解决这个问题。他花了很长时间研究了地形图和洪水的模式,最终他发现了一种方法:他可以在山脚…

《Redis使用手册之列表》

《Redis使用手册之列表》 目录 **《Redis使用手册之列表》****LPUSH&#xff1a;将元素推入列表左端****LPUSHX、RPUSHX&#xff1a;只对已存在的列表执行推入操作****LPOP&#xff1a;弹出列表最左端的元素****RPOP&#xff1a;弹出列表最右端的元素****RPOPLPUSH&#xff1a;…

C++Day2作业

1、矩形类 #include <iostream>using namespace std;class Rec //类默认私有属性 {int length;int width; public: //设置共有属性void set_length(int l);void set_width(int w);void show();int get_length();int get_width(); };void Rec::set_l…

unity中 UnityWebRequest.Post和 UnityWebRequest uwr = new UnityWebRequest两种方法有什么区别

在Unity中&#xff0c;UnityWebRequest.Post 和 UnityWebRequest uwr new UnityWebRequest(...) 是两种不同的方式来创建和发送HTTP POST请求&#xff0c;但它们之间有一些关键的区别和用法上的差异。 1. UnityWebRequest.Post (静态方法) UnityWebRequest.Post 是一个静态方…

PeLK: 大卷积核强势回归,高达101 × 101,提出了外围卷积

paper&#xff1a;https://arxiv.org/pdf/2403.07589 code&#xff1a;暂无 目录 0. 摘要 1. 引言 2. 相关工作 2.1. Large Kernel Convolutional Networks 2.2. Peripheral Vision for Machine Learning 3. 密集卷积优于条纹卷积 4. 参数高效的大核卷积神经网络 4.1. …

Java_从入门到JavaEE_06

一、方法 1.静态方法 理解&#xff1a;特定功能的代码块 好处&#xff1a;解决代码的冗余 语法结构&#xff1a; 访问修饰符 static 返回值类型 方法名([参数列表]){ ​ …代码块… } 分类&#xff1a; 无参数无返回值的方法 带参数的方法 带返回值的方法 2.无参数无返回值…

EasyDarwin录像存储

目录 1、安装ffmpeg 2、建立录像存储路径 3、修改EasyDarwin配置文件 4、测试 (1)推流&#x

php变量创建和定义规则和常见常量

在 PHP 中&#xff0c;变量是用于存储数据的容器&#xff0c;并且可以根据需要进行更改。以下是 PHP 变量创建和定义的一些基本规则&#xff1a; 变量名以 $ 符号开头&#xff1a;在 PHP 中&#xff0c;所有变量名都以美元符号 $ 开头。例如&#xff0c;$name、$age 等。变量名…