赞
踩
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); @override State<PreviewImage> createState() => _PreviewImageState(); } class _PreviewImageState extends State<PreviewImage> { @override Widget 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}); @override State<PreviewVideo> createState() => _PreviewVideoState(); } class _PreviewVideoState extends State<PreviewVideo> { VideoPlayerController? _controller; late VoidCallback listener; _PreviewVideoState() { listener = () { if (!mounted) { return; } setState(() {}); }; } @override void 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); } @override void dispose() { super.dispose(); _controller!.removeListener(listener); _controller?.pause(); _controller?.dispose(); } @override void didUpdateWidget(covariant PreviewVideo oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.isFocus! && !widget.isFocus!) { // pause _controller?.pause(); } } @override Widget 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)); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。