Electron自定义协议Protocol对web网站做数据交互,使用SSE实时数据推送到网站

news2025/1/18 3:26:22

(防盗镇楼)本文地址:https://blog.csdn.net/cbaili/article/details/128651549

前言

最近在撸VUE,想要实现一份代码既能构建Web又能构建Electron应用
并且能够判断环境是浏览器还是Electron,随后在Electron中做一些特定的事情

以往的Electron通信依靠IPC通信完成,但是发布到Web端运行会报错找不到__dirname
这是因为网页浏览器与Electron不同,Electron集成了Node环境,所以Electron能够访问到.

苦寻良久,终于在一个帖子上发现了解决方案:网页与Electron通信


解决方案

这个方案利用Electron的自定义协议实现.
在前端项目中无需引用任何Electron库即可实现与Electron通信,过程可控,完美!

什么是自定义协议?

标准协议就是http https这类的,自定义协议顾名思义就是允许你定义一个协议,自己来实现通信数据处理过程。

你大概率见到过一些网页唤醒本机应用程序的案例,比如百度网盘、Steam等.它们都是利用了这个机制实现。

你可以自定义一个myapp的协议,通过myAPP://lala/?data=123这样的url访问资源

但我们需要的不同,我们不希望去唤醒应用程序,而是直接让Electron拦截掉这个请求。

来看看API

Electron为我们提供了protocol类,它用于注册自定义协议和拦截自定义请求并允许自定义数据处理过程.

这东西用起来很简单,看代码案例:

//electron项目的index.js
const URL = require('url');
const { app, BrowserWindow, protocol } = require('electron')
const MY_CUSTOM_PROTOCOL_SCHEMA="myapp";//命名需要遵循URL PROTOCOL协议

//#第一步:注册自定义协议,必须在app ready之前完成,且只能调用一次
protocol.registerSchemesAsPrivileged([ { scheme: MY_CUSTOM_PROTOCOL_SCHEMA}])

app.on('ready', () => {
    win = new BrowserWindow({ width: 800, height: 600 })
    win.loadURL('http://127.0.0.1:5173/#/')

    if (!process.env.IS_TEST) win.webContents.openDevTools();
	
	//#第二步:定义刚刚注册的自定义协议返回数据类型
	//protocol.registerBufferProtocol
	//protocol.registerFileProtocol
	//protocol.registerHttpProtocol
	//protocol.registerStreamProtocol
	//protocol.registerStringProtocol
	//这里以返回文本为例
    protocol.registerStringProtocol(MY_CUSTOM_PROTOCOL_SCHEMA, (req, res) => {
    	//我们可以使用node.js的url模块对地址进行解析
    	//参考:https://cloud.tencent.com/developer/article/1653911
    	let url = URL.parse(req.url, true)
    	console.log("myapp:// Requested", req, res,url);
    	//do something it here..
        res("lalala")//返回给网页的数据,你可以做成JSON返回
    })
})
//前端js调用部分
callMyapp("asd/ccc?eee=123")
function callMyapp(path){
	const MY_CUSTOM_PROTOCOL_SCHEMA="myapp"
	const url=`${MY_CUSTOM_PROTOCOL_SCHEMA}://${path}`

    //构建get请求(axios不支持自定义协议会报错)
	var httpRequest = new XMLHttpRequest();
  	httpRequest.open('GET', url, true);
    httpRequest.send();
  	httpRequest.onreadystatechange = () => {
    if (httpRequest.readyState == 4 && httpRequest.status == 200) {
      console.log(httpRequest.responseText);
    }
  };
}

以上就实现了简单的通信请求,我们可以通过在Electron中判断请求URL做一些事情了.
还有,URL最大支持2083个字符,所以你要传递的数据文本长度不可以超过这个数字…


Electron持续发送数据到Web(SSE技术)

上面我们实现了HTML->Electron的数据发送,但有时我们还需要将Electron中产生的事件发送到HTML中。

总归来讲大概有三种方式:ajax轮训 、Socket通信、SSE服务器推送事件(Server-sent Events).
前两者不说了,前者不友好后者太鸡肋。我们一起来看看这个SSE:

SSE使用数据流传输数据
这是一种服务器向网页单向持续传输数据的机制
当网页向服务器发送一个请求时,服务器返回的是一个数据流,这个流会让会话一直保持连接.
这常见于文件下载、视频传输等场景.

因此我们可以创建一个自定流,然后随时随地的 往里面写各种数据.
遗憾的是Edge/IE没有这个服务的实现,但有第三方库eventsource使用纯JS实现了这个方法.
不过此文我们用到SSE的地方是在Electron环境里呀,它可是Chrome内核的所以放心搞就好了,浏览器环境中运行不到这部分代码所以不会报错.

上代码:

//Electron端
import STREAM from 'stream';
const { app, BrowserWindow, protocol,ProtocolResponse } = require('electron')

const MY_CUSTOM_PROTOCOL_SCHEMA="myapp-sse";//命名需要遵循URL PROTOCOL协议

//#第一步:注册自定义协议,必须在app ready之前完成,且只能调用一次
protocol.registerSchemesAsPrivileged([ { scheme: MY_CUSTOM_PROTOCOL_SCHEMA}])

app.on('ready', () => {
	//#第二步:注册一个流自定义协议
	protocol.registerStreamProtocol(MY_CUSTOM_PROTOCOL_SCHEMA, (req, res) => {
	    const stream = new STREAM.PassThrough()
	    stream.on("close", () => {/*网页关闭或链接中断时触发*/stop() })
	    stream.on("error", (err) => {/*异常时触发*/ stop() })
	
	    //#第三步:构造HTTP流协议响应数据并响应请求
	    res({
	        statusCode: 200,
	        mimeType: "text/event-stream",
	        headers: {
	            'Cache-Control': 'no-cache',
	            'Connection': 'keep-alive',
	            'Access-Control-Allow-Origin': '*',
	        },
	        data: stream,// PassThrough 也是一个 ReadableStream,可以设置给data
	    })
		
		//#第四步:持续向流中写入数据
	    const timer = setInterval(() => { send("传递的数据", "message") }, 1000)// 每秒发送一次数据
	
		//封装一些方法
		let msgID = 0//消息ID,每次发送新数据时自增1
	    let _timeout = 3000//链接超时,如果超过这个时间没有数据传输,浏览器则会自动断开链接,所以我们需要定时发送一个数据来保持链接
	    function stop() { clearInterval(timer) }//当网页关闭或链接中断时停止发送数据
	    function send(data, event, id = undefined) {
	        //构造SSE响应数据
	        //它有一个标准的格式,每一行都是以\n结尾,每一行的格式为: key: value\n
	        if (id == undefined) msgID++;
	        let ret = `data: ${data}\n`//传递的数据,可以是任意类型,但是必须是字符串,如果是对象,则需要JSON.stringify()转换为字符串
	        ret += `event: ${event}\n` //事件类型:默认为message,可以自定义
	        ret += `id: ${id || msgID}\n` //消息ID:每次发送新数据都应该自增,用于浏览器判断是否有新数据和数据重发
	        ret += `retry: ${_timeout}\n`//重试时间
	        ret += `\n`//数据的结尾必须是两个\n
	        stream.push(ret)//将数据写入流,随后浏览器就会收到数据
	    }
	})
})
//网页端部分代码
//Electron环境判断
const isElectron = /electron/i.test(navigator.userAgent);

//有了环境判断以后我们就可以避免在浏览器中执行SSE请求了
if (isElectron && typeof (EventSource) !== 'undefined') {
    //构造请求地址
    const MY_CUSTOM_PROTOCOL_SCHEMA="myapp-sse"
    const type = "aa"
    const parm = { dd: "ff" }
    const event = "ccc"
    const url = `${MY_CUSTOM_PROTOCOL_SCHEMA}://${type}/?event=${event}&parm=${btoa(JSON.stringify(parm))}`

    //构造EventSource(浏览器SSE请求)
    const evtSource = new EventSource(url, { withCredentials: true }) // 后端接口,要配置允许跨域属性
    // 与事件源的连接刚打开时触发
    evtSource.onopen = function (e) {
        console.log("sse onopen", e);
    }
    // 当从事件源接收到数据时触发
    evtSource.onmessage = function (e) {
        console.log("sse onmessage", e);
    }
    // 与事件源的连接无法打开时触发
    evtSource.onerror = (e: Event) => {
        console.log("sse onerror", e);
        evtSource.close(); // 关闭连接
    }
    //Electron代码中send函数内自定义的Event
    evtSource.on("自定义Event", (e: Event) => {
        console.log("sse custom event", e);
    })
} else {
    console.log("sse not support", '当前浏览器不支持使用EventSource接收服务器推送事件!');
}

上面的代码可能需要花费一些时间才会理解,阅读下列文章可以帮助你快速整明白

  • 除了 Websocket ,服务端还有什么办法能向浏览器主动推送信息
  • SSE(Server Sent Events) HTTP服务端推送详解
  • SSE技术详解:使用 HTTP 做服务端数据推送应用的技术

由此,我们完成了Electron端向Web端持续发送消息的机制,这特别适合一些事件的即时响应.


小拓展

判断网页是否运行在自己的APP中

上面的前端代码实现了判断是否运行在Electron中
但运行的Electron不一定是我们自己的app,它内部没有我们注册的自定义协议,这样会导致调用请求的时候报错

所以我们还需要更精准的判断:
我们可以通过查看输出navigator.userAgent,发现其中有一段包含了我们app的名字和版本号
在这里插入图片描述
所以,我们可以通过修改上面的判断条件实现

const isRunInApp = /YOUR_APP_NAME/i.test(navigator.userAgent)

安全考虑

这个方案显然还有一些安全上的问题需要处理,比如:

  • 接口来源认证:不能说随便一个网站用了这个方法就都能调用我们APP的接口
  • 暴力请求过滤:防止系统资源耗尽
  • 加密敏感数据:如非必要不要传输这些个啥
  • 防注入等等…

所以我们尽量不要在接口中进行一些高危操作,如果要有必须要加验证机制.


最终

有了以上两部分内容,我们可以针对网站运行在我们自己的客户端情况下,做出一些特殊功能支持,比如一些情景:

  • 模拟窗口:在网页浏览器中不显示标题栏按钮 但在Electron中显示并能够让Election响应最大化最小化关闭等按钮事件
  • 绕过一些安全限制:可以从Elecron访问文件系统、操作硬件之类的(注意安全)
  • 为网站提供一些本地化信息

话说,这个文章标题真难起…
好用别忘了一按三联哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158628.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

模板(template)包含与继承

Django 模板查找机制: Django 查找模板的过程是在每个 app 的 templates 文件夹中找(而不只是当前 app 中的代码只在当前的 app 的 templates 文件夹中找)。各个 app 的 templates 形成一个文件夹列表,Django 遍历这个列表&#x…

超详细的Socket通信原理和实例讲解

我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket&am…

【算法篇-排序】八大排序

十大排序0.常见排序1. 插入排序(直接插入排序和希尔排序)1.1直接插入排序1.2希尔排序(缩小增量排序)2.选择排序2.1选择排序2. 2堆排序3.交换排序3.1 冒泡排序3.2快速排序3.2.1hoare版本快排3.2.2挖坑法3.2.3前后指针法3.3.4 快排的…

【Linux】在Linux上写一个进度条小程序

👑作者主页:安 度 因 🏠学习社区:安度因的学习社区 📖专栏链接:Linux 文章目录一、前言二、理解 \r 与 \n三、行缓冲1、提出问题2、认识行缓冲3、解答与拓展4、倒计时四、进度条五、结语如果无聊的话&#…

2023/1/12总结

今天学习了图的割点与桥的算法 图的割点以及桥 图的割点:割点是指在无向连通图中,某点和该点连接的边去掉以后图便不再连通 在上面的图片中(上面是一个有向图,我们当作无向图即可)我们知道当我们去掉A点之后&#xf…

进阶必看 | 6个让Revit建模起飞的习惯,高效就靠它

大家好,这里是建模助手。 相信各位都知道,建模助手一向以来都追求更高,更快,更强。但是有些问题,不是插件本身能解决的事情,而是项目本身的问题。 一般来说,当Revit项目模型大于150MB时&#…

Linux安装sonarqube(含各种错误处理)

目录 1.下载安装 2.错误处理 2.1.JDK版本不适配 2.2.can not run elasticsearch as root 1.下载安装 下载地址: Download | SonarQube | Sonar (下载页面向下拉)选择稳定版本下载。 解压后启动脚本在: bin/{对应操作系统}…

【dp】买卖股票的最佳时机系列题目

文章目录121. 买卖股票的最佳时机122. 买卖股票的最佳时机 II309. 最佳买卖股票时机含冷冻期123. 买卖股票的最佳时机 III188. 买卖股票的最佳时机 IV121. 买卖股票的最佳时机 本题的重点是:只能在前面某一天买入,后面某一天卖出。要不就是不买入&#x…

外贸业务员怎样能提高自己的工作能力?

关于外贸业务员提高自己的工作能力,米贸搜整理如下,希望可以帮助到你:1.树立一个好的目标,并坚定不移地朝着这个目标努力。这个问题,无论你是新手还是有经验的外贸业务员,相信每个外贸业务员都或多或少的思…

K_A11_004 基于STM32等单片机采集热敏传感参数串口与OLED0.96双显示

K_A11_004 基于STM32等单片机采集热敏传感参数串口与OLED0.96双显示一、资源说明二、基本参数参数引脚说明三、驱动说明IIC地址/采集通道选择/时序对应程序:四、部分代码说明1、接线说明1.1、STC89C52RC热敏传感模块1.2、STM32F103C8T6热敏传感模块五、基础知识学习与相关资料下…

NCS8823替代方案|CS5260Typec转VGA可替代NCS8823|低BOM成本替代NCS8823设计

NCS8823替代方案|CS5260Typec转VGA可替代NCS8823|低BOM成本替代NCS8823设计 NCS8823是一款低功耗、DisplayPort信号至VGA转换器,通过USB Type-C连接器。它是 适用于USB Type-C至VGA转换器,适配器、对接设备。此设备结合了基于USB Type-C的 DisplayPort接收器和VGA…

华为私有云平台FusionCompute搭建

一、FusionCompute架构 架构CNA作为虚拟化操作系统,VRM作为虚拟化管理平台正常主机都安装CNA,单独建立VRM集群作为管理集群,我测试环境就一台主机,所以CNA和VRM装在同一台主机上,并且用这台主机分配虚拟机进行测试。 …

前端基础(十二)_函数高级、全局变量和局部变量、 预解析(变量提升)、函数返回值

作用域 作用域指:变量或函数的有效使用范围,有全局作用域与局部作用域两种。 全局变量和局部变量 全局变量:直接在 script 标签下声明的变量,任何地方都能访问,任何地方都能对其值进行改变。 局部变量:函…

CAN总线的个人理解

部分内容可以参考:https://blog.csdn.net/xwwwj/article/details/105372234? CAN概念简介 CAN是Controller Area Network 的缩写 CAN协议经过ISO标准化后有两个标准:ISO11898标准和ISO11519-2标准。其中ISO11898是针对通信速率为125Kbps~1Mbps的高速通…

适合制造业的ERP推荐?使用ERP系统的好处有哪些?

对于制造型企业来说,除了涉及到产品的生产制造和原料采购,还需要管理库存、销售、财务等方方面面。制造业的ERP系统的使用,尤为重要。一个好的制造业的ERP系统在企业管理中起到至关重要的作用,针对制造业的ERP系统提供贴合行业特性…

用cmd命令窗口运行第一个java程序同时分享idea写的代码用cmd编译运行【建议收藏】

在上一篇文章https://blog.csdn.net/qq_52545155/article/details/128651296?spm1001.2014.3001.5502教大家安装了jdk版本,那么我们来编写一个java程序,通过cmd命令运行起来看看效果叭!!! 一、基本代码准备 1、打开记…

超全的SQL注入姿势总结

目录 常见姿势 环境搭建 信息收集 报错注入 延时注入 布尔注入 堆叠注入 绕过方法 绕过引号 or and xor not绕过 绕过注释符 内联注释绕过 类型转换绕过 绕过 WAF绕过-应用层 常见姿势 环境搭建 use mysql; create table if not exists my_table( id int PRIMA…

HC小区管理系统安装记录一次群里小伙伴梓豪方式安装问题

记录一次群里小伙伴安装,供大家参考 问题排查 打开梓豪地址查看 Redis MySQL Nginx 是否启动成功,查看日志启动成功 MySQL正常 redis 没有报错 Nginx也正常 查看hc 是否启动成功,点击控制台 查看docker 发现8008 端口被占用了&#xff0c…

【异常】原来提示SocketTimeoutException:connect timed out还可能是外部因素导致

一、现象截图 一大早收到ELK的邮件提醒,让我来看看,又是哪个妖怪在作孽? 二、问题定位 2.1 SocketTimeoutException:connect timed out 经验告诉我,这个问题一般是第三方平台的问题,大部分原因是发起Http请求&…

惠普M329打印机更换副厂硒鼓后提示墨粉不足并无法打印

买了一个惠普M329打印机,打印效果不错,速度快,大量复印比较方便。因为最近打印和复印比较多,很快原装墨粉用完了。又买了一个副厂(带芯片)的硒鼓换上。不到一个月,又用光了,这次买了同一个副厂的硒鼓(不带芯片)。将原来的芯片(副厂的)拆下来,装在新硒鼓上。装到M3…