当前位置: 首页> 文旅> 酒店 > 常州网站制作培训_宁波网站设计价格_广州网站建设推广专家_赣州seo推广

常州网站制作培训_宁波网站设计价格_广州网站建设推广专家_赣州seo推广

时间:2025/7/11 17:40:45来源:https://blog.csdn.net/m0_46983722/article/details/144455183 浏览次数:0次
常州网站制作培训_宁波网站设计价格_广州网站建设推广专家_赣州seo推广

前言

由于Flutter在双端的开发体验几乎接近的情况下,尝试将Flutter代码转Web端进行部署和发布,在其中遇到的所有问题,我都会通过这种方式分享出来。那么第一个要解决的就是上传资源到TOS上,在双端中都是通过插件的方式在各端通过插件使用不同的SDK去解决上传问题,那么为了改动最小化,web端同样希望使用插件的方式去解决上传问题。

官方链接

tos使用Browser.js SDK去进行上传

https://www.volcengine.com/docs/6349/127737

这次使用的是普通上传的方式

https://www.volcengine.com/docs/6349/127739

分析

将过程进行拆解

流程分析

总流程
在这里插入图片描述

  • Flutter端通过请求获取临时密钥
  • 将密钥交付给web插件,web插件完成初始化工作
  • 用户触发文件选择,将文件交付给web插件
  • web插件获取到文件以及完成初始化工作之后,使用tos sdk发出请求
  • 将结果反馈给Flutter端,完成流程

问题分析

文件上传问题

在双端,可以通过文件选择器file_picker去触发系统的文件管理,这种方式可以获取到临时文件路径,当然也可以通过写入应用沙箱环境获取应用沙箱文件路径,将路径传递给插件处理即可,但是在web端,本质是处于浏览器环境,浏览器环境是不允许直接访问本地文件系统的,也就无法直接获取到任何路径。,我们使用原生html的input上传文件也可以进行验证,能够得到的是一个File对象,再者,web端不仅仅包括H5,还包括各家小程序,不同小程序提供的web环境,文件系统的管理是受各自小程序的管控的,因此这项工作应该交给web插件去处理,判断具体的环境情况,是获取File对象还是通过不同小程序提供的开放能力获取临时文件路径。

Dart和JS互相调用

Dart是强语言,JS是弱语言,没有类型的概念,那么我写了JS插件之后,如何在Dart层提前感知到相应的插件。
Flutter SDK在3.3.0之后,推出两个标准库去处理这样的问题。

JavaScript interop:基于扩展类型的新 JS 互操作机制,当针对 JavaScript 和 Wasm 时,可以在 Dart 代码、浏览器 API 和 JS 库之间进行简洁、类型安全的调用,Dart 开发人员可以访问类型化 API 来与 JavaScript 交互,API 通过静态强制明确定义了两种语言之间的边界,在编译之前消除了许多问题。
package:web:能够直接操作dom相关。

我们需要使用这两个库去解决问题。

编写插件

创建web插件模版

使用命令

flutter create --template=plugin --platforms=web .

这个时候可能会得到一个错误

Ambiguous organization in existing files: {com.example}. The --org command line argument must be specified to recreate project.

这个时候是因为当前的库有了其他端的测试用例,重新生成example文件夹即可。

这个时候会在lib目录下面生成一个文件。

根据个人习惯,我也阅读了其他插件,都会把这份文件放在lib的src目录下面。

配置pubspec.yaml文件

在dependencies下面加入:
同时web的依赖也要添加,便于我们后续操作dom

dependencies:、、、web: ^1.1.0flutter_web_plugins:sdk: flutter、、、

运行测试

我们在example文件夹下面点击lib的main.dart选择chrome进行运行即可。

选择文件

我们实际的逻辑就在生成的文件下进行编写。

我们首先解决选择文件的问题。

这里仅提供逻辑参考,假如在h5端,本质就是创建一个input元素,模拟点击即可。在小程序就使用其他端的sdk去选择文件,得到的是File对象,比如在小程序,就是wx.chooseMedia的方式直接获取到临时路径

//  wx.chooseMedia({
//   count: 9,
//   mediaType: ['image','video'],
//   sourceType: ['album', 'camera'],
//   maxDuration: 30,
//   camera: 'back',
//   success(res) {
//     console.log(res.tempFiles[0].tempFilePath)
//     console.log(res.tempFiles[0].size)
//   }
// })
  Future<dynamic> pickWebFile() async {// 判断是否在微信小程序环境if (isWeChatMiniProgram()) {// 微信小程序逻辑处理return await pickFileInWeChatMiniProgram();} else {// 非微信小程序环境下,使用浏览器的文件选择器final input = web.document.createElement('input') as web.HTMLInputElement;input.type = 'file'; // 设置为文件选择类型input.accept = '*/*'; // 可选:限制选择的文件类型,例如 'image/*''.txt'input.click();// 等待文件选择完成await input.onChange.first;if (input.files != null) {// 返回选中的第一个文件return input.files!;}}// 如果未选择文件或处理失败,返回 nullreturn null;}

上传文件

JS插件

这里我是根据官方文档去编写的js代码

这里我创建了一个类

  • 提供一个初始化方法init函数
  • 提供获取当前web环境的函数
  • 提供一个上传文件的方法uploadFiles函数

class TosPluginJx {static instance; // 定义静态属性来保存单例实例constructor(client, dir, host) {this._client = client;this._dir = dir;this._host = host;}get host() {return this._host;}set host(host) {this._host = host;}get client() {return this._client;}set client(client) {this._client = client;}get dir() {return this._dir;}set dir(dir) {this._dir = dir;}static getInstance() {if (!TosPluginJx.instance) {TosPluginJx.instance = new TosPluginJx();}return TosPluginJx.instance;}checkOut(data) {if (!data.accessKeyId) {return false}if (!data.secretAccessKey) {return false}if (!data.sessionToken) {return false}if (!data.region) {return false}if (!data.bucket) {return false}if (!data.dir) {return false}if (!data.host) {return false}return true}init(data) {if (!this.checkOut(data)) {console.log("tos_sdk_初始化失败", data)return false}this.dir = data.dirthis.host = data.hostthis.client = new TOS({// 从 STS 服务获取的临时访问密钥 AccessKeyIdaccessKeyId: data.accessKeyId,// 从 STS 服务获取的临时访问密钥 AccessKeySecretaccessKeySecret: data.secretAccessKey,// 从 STS 服务获取的安全令牌 SessionTokenstsToken: data.sessionToken,// 填写 Bucket 所在地域。以华北2(北京)为例,Region 填写为cn-beijingregion: data.region,// 填写 Bucket 名称bucket: data.bucket,});console.log("tos_sdk_初始化完成", this.client, data)return true}//获取当前web环境getEnv() {if (window.WeixinJSBridge) {return 'wxMiniProgram'}return 'h5'}/// 上传文件//返回一个数组回去async uploadFiles(data) {try {console.log("开始上传", data)console.log("上传路径为", this.dir)if (data.length == 0) {console.log("当前文件为空")return [{fileStr: '',uuid: data[0].uuid,downloadUrl: '',isCompleted: true,msg: '当前文件为空',code: 0}];}if (data[0].fileStr) {console.log("当前为文件路径", data[0].fileStr)const result = await this.client.putObject({key: this.dir + data[0].fileStr,body: data[0].fileStr ? data[0].fileStr : data[0].fileBlob,// headers,});console.log("上传结果", result)if (result.statusCode == 200) {return [{fileStr: '',uuid: data[0].uuid,downloadUrl: this.host + this.dir + data[0].fileStr,isCompleted: true,msg: '上传成功',code: 1}];}} else if (data[0].fileBlob) {console.log("当前为文件对象", data[0].fileBlob)const result = await this.client.putObject({key: this.dir + data[0].fileBlob.name,body: data[0].fileBlob,});console.log("上传结果", result)if (result.statusCode == 200) {return [{fileStr: '',uuid: data[0].uuid,downloadUrl: this.host + this.dir + data[0].fileBlob.name,isCompleted: true,msg: '上传成功',code: 1}];}}return [{fileStr: '',uuid: data[0].uuid,downloadUrl: '',isCompleted: true,msg: '上传失败',code: 0}];} catch (e) {console.log(e)return [{fileStr: '',uuid: data[0].uuid,isCompleted: true,downloadUrl: '',msg: '上传失败,' + e,code: 0}];}}}globalThis.TosPluginJx = TosPluginJx.getInstance(); //对外暴露单例,这里非常重要

Flutter端

在web编写插件,不像ios或者安卓,有channal管道的概念。

但是我们需要提前声明我们要使用的Js类以及函数以及我们需要使用的数据类型

这里我声明了我要使用的类TosPluginJx,里面有三个函数,分别接收什么类型的参数,返回什么类型的数据

我们使用的就是JavaScript interop提供的能力。

还记得我们上面暴露到全局的TosPluginJx吗,在这里,通过注解@JS去进行标注,告诉Flutter层在web环境中有这样的类。

在Js中类的本质就是一个object,将所有类型定义出来。


//声明tos要获取的数据类型
extension type TosInfoType._(JSObject _) implements JSObject {external JSString accessKeyId;external JSString secretAccessKey;external JSString sessionToken;external JSString host;external JSString region;external JSString endpoint;external JSString bucket;external JSString dir;//转换层factory TosInfoType.fromMap(Map param) {final obj = JSObject();return TosInfoType._(obj)..accessKeyId = param['AccessKeyId']..bucket = param['Bucket']..dir = param['_dir_']..endpoint = param['Endpoint']..host = param['Host']..region = param['Region']..secretAccessKey = param['SecretAccessKey']..sessionToken = param['SessionToken'];}
}//声明要获取的文件类型
extension type FileInfoType._(JSObject _) implements JSObject {external JSString uuid;external JSString? fileStr;external JSAny? fileBlob;external JSAny? ext;external JSString? downloadUrl;external JSBoolean? isCompleted;external JSString? msg;external JSNumber? code;//转换层factory FileInfoType.fromMap(Map param) {final obj = JSObject();return FileInfoType._(obj)..uuid = param['uuid']..fileStr = param['fileStr']..fileBlob = param['fileBlob']..ext = param['ext']..downloadUrl = param['downloadUrl']..isCompleted = param['isCompleted']..msg = param['msg']..code = param['code'];}//转成mapMap toMap() {return {'uuid': uuid,'fileStr': fileStr,'fileBlob': fileBlob,'ext': ext,'downloadUrl': downloadUrl,'isCompleted': isCompleted,'msg': msg,'code': code};}
}//声明要操作的类
extension type TosPluginJx._(JSObject _) implements JSObject {external TosPluginJx();external JSBoolean init(TosInfoType tosInfo);external JSPromise<JSArray<FileInfoType>> uploadFiles(JSArray<FileInfoType> fileInfo);external JSString getEnv();
}@JS('window.TosPluginJx') //标识全局对象
external TosPluginJx get tosPluginJx;

调用js sdk

在上面我们已经定义好了TosPluginJx类所涉及的所有需要使用的类型、方法、返回值。

现在我们可以直接进行使用。

需要注意一点,从js层拿到的是js类型,需要通过toDart进行转换

反之通过toJS

//初始化
tosPluginJx.init(info);//获取环境String current = tosPluginJx.getEnv().toDart;//上传文件
//将客户端传过来的参数转成jsfinal jsFileArray =files.map((file) => FileInfoType.fromMap(file)).toList().toJS;print(jsFileArray);//传递给js插件层JSArray<FileInfoType> result =await tosPluginJx.uploadFiles(jsFileArray).toDart;//拿到结果再转换dart层List finl = result.toDart.map((e) => e.toMap()).toList();

打包测试

由于上传可能有tos的跨域问题,所以我们打包上传之后再进行传输。

执行命令

flutter build  web

注意:生成的index.html要把base的href进行修改,否则找不到资源

    <base href="">

结论

至此,我们完成Flutter_web插件的编写,Flutter提供了两个库去操作dom和js层,但我们的场景是需要使用第三方的js,因此又涉及到了dart层和js层互相调用的地方,两者在类型不同的情况下,如何做到类型兼容,下一篇将解决Flutter转web之后,webview是如何处理的,数据是如何交互的。

如果有更好的想法,欢迎提出。

关键字:常州网站制作培训_宁波网站设计价格_广州网站建设推广专家_赣州seo推广

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: