创建项目
代码语言:javascript复制git clone https://gitee.com/psvmc/electron-quick-start.git
# 重命名
ren electron-quick-start electron-flash-demo
cd electron-flash-demo
# 删除原来的git文件夹
rmdir /s/q .git
rmdir /s/q .github
npm install
npm start
下载32位的Electron
项目根目录中添加.npmrc
arch=ia32
registry=https://registry.npm.taobao.org
设置Flash插件
下载插件
下载pepflashplayer插件
注意
这个插件一定要用老版本,新版本的flash由于中国代理商要赚钱,会检测一个服务是否启动,不启动就会报错,强行让更新新版本。
如图
老版本的Chrome下载 里面带有pepflashplayer插件
https://www.slimjet.com/chrome/google-chrome-old-version.php
这里推荐下载32位的最老的版本
Version | Size | Date |
---|---|---|
48.0.2564.97 | 40.76 MB | 2020-04-29 |
这个插件已经很难下载到了,我的方法是下载个360浏览器带极速内核的版本,打开一个带有flash的网页,它就会自动下载插件
在浏览器的安装目录下搜索pepflashplayer
,就会找到对应的dll文件。
或者下载下面的DLL
pepflashplayer32_20_0_0_286.dll
链接:https://pan.baidu.com/s/1_eMRkJ8m6jILi40BHSH-4Q 提取码:psvm
注意
这个插件是32位的,一定要保证Electron是32位的。
配置插件
把下载的插件放在项目根目录下libs
文件夹下,如图所示:
main.js中添加以下代码
代码语言:javascript复制let pluginName
switch (process.platform) {
case 'win32':
pluginName = 'pepflashplayer.dll'
break
case 'darwin':
pluginName = 'PepperFlashPlayer.plugin'
break
case 'linux':
pluginName = 'libpepflashplayer.so'
break
}
let plugins_path = path.join(__dirname, "libs", "ppflash", pluginName);
if (__dirname.includes(".asar")) {
plugins_path = path.join(process.resourcesPath, "libs", "ppflash", pluginName)
}
app.commandLine.appendSwitch('ppapi-flash-path', plugins_path);
注意:
这里一定要进行判断,因为打包前后的路径是不一致的。
不显示菜单栏
代码语言:javascript复制const electron = require('electron')
/*获取electron窗体的菜单栏*/
const Menu = electron.Menu
/*隐藏electron创听的菜单栏*/
Menu.setApplicationMenu(null)
页面配置
官方文档:
- https://www.electronjs.org/zh/docs/latest/api/webview-tag
- https://www.electronjs.org/zh/docs/latest/api/web-contents
可用于测试Flash的页面:https://sc.chinaz.com/donghua/220315391630.htm
方式1
这种方式最为简单。
主进程 BrowserWindow
BrowserWindow添加webPreferences配置
代码语言:javascript复制const mainWindow = new BrowserWindow({
width: 1366,
height: 768,
webPreferences: {
webviewTag: true,
javascript: true,
plugins: true,
webSecurity: false
}
})
plugins: true
这个配置项是必须的。
如果使用的是webview
,在标签里添加 plugins
属性。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Flash</title>
</head>
<body>
<webview src="https://sc.chinaz.com/donghua/220315391630.htm" allowpopups plugins></webview>
<style lang="text/css">
body {
margin: 0;
padding: 0;
}
webview {
display: flex;
width: 100vw;
height: 100vh;
}
</style>
</body>
</html>
请注意,webview
标签的样式使用 display:flex;
来确保 iframe
在传统和 flex 布局一起使用的情况下填充其 webview
容器的全部高度和宽度。
在devtools的控制台输入以下命令检查Pepper Flash插件是否被加载。
代码语言:javascript复制navigator.plugins
注意
这个只能判断是否加载插件,不能判断插件是否可用,比如没有dll就不可用,但是插件列表中已经存在。
方式2
这种方式能控制访问的连接。
首先我们看四种打开新页面的方式
代码语言:javascript复制<a href="https://www.psvmc.cn" target="_blank">_blank</a>
<span onclick="javascript:window.open('https://www.psvmc.cn')">open</span>
分别是:
_blank
.open
实际运行情况是:
在正常的浏览器中,这两种情况都是能新开窗口的。但是,部分浏览器里面可能会拦截.open
这种方式。但是绝对没有任何浏览器会拦截_blank
这种。
在electron
的webview
中,
- 对于
_blank
是默认拦截的,不会自动打开。 - 对于
.open
, 添加allowpopups
就会自动用新窗口打开。
所以添加allowpopups
属性,就可以解决面的情况,
_blank
的页面添加allowpopups
后也无法打开
为了保证两种方式都能正常打开,页面中添加JS,注意
这时候不要添加
allowpopups
,否则会打开两个页面。 因为new-window
能同时监听到这两种方式。
示例
代码语言:javascript复制<webview src="https://sc.chinaz.com/donghua/220315391630.htm" plugins></webview>
<script>
onload = () => {
const webview = document.querySelector('webview')
const new_window = (e) => {
const protocol = (new URL(e.url)).protocol;
if (protocol === 'http:' || protocol === 'https:') {
//webview.loadURL(e.url)
if(e.url.indexOf("sc.chinaz.com")!==-1){
window.open(e.url)
}
}
}
const will_navigate = (e) => {
const protocol = (new URL(e.url)).protocol;
if (protocol === 'http:' || protocol === 'https:') {
console.info("will_navigate",e.url);
if(e.url.indexOf("sc.chinaz.com")===-1){
webview.reload();
}
}
}
webview.addEventListener('new-window', new_window);
webview.addEventListener('will-navigate', will_navigate);
}
</script>
注意
new-window
只能监听到页面内_blank
和.open
,页面的重定向是监听不到的。
方式3
这种方式不但能够控制访问的连接,还能设置窗口属性。
默认的方式其实也是新的进程,和下面的方式一样,但是这种方式我们可以做一些窗口属性的设置。
渲染进程
代码语言:javascript复制<script>
const ipcRenderer = window.require('electron').ipcRenderer
onload = () => {
const webview = document.querySelector('webview')
const loadstart = () => {
console.log("开始加载");
}
const loadstop = () => {
console.log("加载完成");
}
const new_window = (e) => {
const protocol = (new URL(e.url)).protocol;
if (protocol === 'http:' || protocol === 'https:') {
//window.open(e.url)
ipcRenderer.send('open_url',e.url);
}
}
webview.addEventListener('did-start-loading', loadstart);
webview.addEventListener('did-stop-loading', loadstop);
webview.addEventListener('new-window', new_window);
}
</script>
主进程
代码语言:javascript复制let mainWindow;
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1366,
height: 768,
webPreferences: {
webviewTag: true,
javascript: true,
plugins: true,
webSecurity: false,
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false,
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
//mainWindow.webContents.openDevTools()
}
const ipcMain = require('electron').ipcMain
let new_win;
ipcMain.on('open_url',(event, arg)=>
{
new_win = new BrowserWindow({
width: 1366,
height: 768,
title: "",
webPreferences: {
webviewTag: true,
javascript: true,
plugins: true,
webSecurity: false
},
frame:true,
parent: mainWindow,
})
new_win.loadURL(arg);
new_win.on('closed',()=>{new_win = null})
})
注意
主窗口要设置Node环境
nodeIntegration: true,
禁止外链跳转
方式1
按照逻辑我们要限制跳转,只要监听will-navigate
事件,阻止它e.preventDefault();
就行了,但是实际上这并没有用。
如下(这样写并不生效)
代码语言:javascript复制const will_navigate = (e) => {
const protocol = (new URL(e.url)).protocol;
if (protocol === 'http:' || protocol === 'https:') {
console.info("will_navigate",e.url);
if(e.url.indexOf("sc.chinaz.com")===-1){
e.preventDefault();
}
}
}
webview.addEventListener('will-navigate', will_navigate);
原因是
所有的
event.preventDefault()
都应该从主进程中呼叫而不是渲染进程。
所以我们就要在主进程中做如下操作
- 最外层
BrowserWindow
的webContents
上监听did-attach-webview
事件,获取新挂上去的<webview>
的webContents
。 - 使用获取到的
webContents
监听will-navigate
事件。
这时候,我们就可以在 will-navigate
事件中使用 e.preventDefault()
阻止 <webview>
导航至其他网页了
代码如下:
代码语言:javascript复制mainWindow.webContents.on('did-attach-webview', (e, wc)=>{
const will_navigate = (e, url)=>{
console.info(url);
if (url.indexOf("sc.chinaz.com") === -1) {
e.preventDefault();
wc.reload();
}
}
wc.on('will-navigate', will_navigate);
})
方式2
当然换个思路,虽然我们阻止不了,我们直接重新加载不就行了吗。
代码语言:javascript复制onload = () => {
const webview = document.querySelector('webview')
const new_window = (e) => {
const protocol = (new URL(e.url)).protocol;
if (protocol === 'http:' || protocol === 'https:') {
//webview.loadURL(e.url)
if(e.url.indexOf("10.88.8.90:9080")!==-1){
window.open(e.url)
}
}
}
const will_navigate = (e) => {
const protocol = (new URL(e.url)).protocol;
if (protocol === 'http:' || protocol === 'https:') {
console.info("will_navigate",e.url);
if(e.url.indexOf("10.88.8.90:9080")===-1){
webview.reload();
}
}
}
webview.addEventListener('new-window', new_window);
webview.addEventListener('will-navigate', will_navigate);
}
我的方案
下面的两种方案 我最终的选择是
上面页面配置的方式3和禁止跳转的方式1相结合,这样就可以同时处理主页面和子页面的跳转限制。
主进程
代码语言:javascript复制let mainWindow;
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1366,
height: 768,
webPreferences: {
webviewTag: true,
javascript: true,
plugins: true,
webSecurity: false,
nodeIntegration: true,
enableRemoteModule: true,
contextIsolation: false,
}
})
// and load the index.html of the app.
mainWindow.loadFile('index.html')
// Open the DevTools.
//mainWindow.webContents.openDevTools()
addListener(mainWindow);
}
const ipcMain = require('electron').ipcMain
let new_win;
ipcMain.on('open_url',(event, arg)=>
{
new_win = new BrowserWindow({
width: 1366,
height: 768,
title: "",
webPreferences: {
webviewTag: true,
javascript: true,
plugins: true,
webSecurity: false
},
frame:true,
parent: mainWindow,
})
new_win.loadURL(arg);
new_win.on('closed',()=>{new_win = null})
addListener(new_win);
})
function addListener(win){
//主页面中的重载
win.webContents.on('did-attach-webview', (e, wc)=>{
const will_navigate = (e, url)=>{
if (url.indexOf("10.88.8.90:9080") === -1) {
e.preventDefault();
wc.reload();
}
}
wc.on('will-navigate', will_navigate);
})
// 子页面中的重载
const will_navigate = (e, url)=>{
if (url.indexOf("10.88.8.90:9080") === -1) {
e.preventDefault();
win.webContents.reload();
}
}
win.webContents.on('will-navigate', will_navigate);
}
渲染进程
代码语言:javascript复制<webview src="http://10.88.8.90:9080/AtomLocal/common/login.faces" disablewebsecurity plugins></webview>
<script>
const ipcRenderer = window.require('electron').ipcRenderer
onload = () => {
const webview = document.querySelector('webview');
let lastUrl = "";
const new_window = (e) => {
// 防止同样的地址,多次调用
if(!lastUrl){
const protocol = (new URL(e.url)).protocol;
if (protocol === 'http:' || protocol === 'https:') {
if(e.url.indexOf("10.88.8.90:9080")!==-1){
ipcRenderer.send('open_url',e.url);
lastUrl = e.url;
setTimeout(()=>{
lastUrl = "";
},1000)
}
}
}
}
webview.addEventListener('new-window', new_window);
}
</script>
打包
添加依赖
代码语言:javascript复制npm install electron-builder@22.9.1 --save-dev
在pakage.json中,我们build的配置下面内容:
代码语言:javascript复制{
"name": "flashapp",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"start": "electron .",
"dist": "electron-builder --win --ia32",
"dist_dir": "electron-builder --win --ia32 --dir"
},
"build": {
"appId": "cn.psvmc.flashapp",
"productName": "Flash加载",
"icon": "app.ico",
"asar": true,
"files": [
"main.js",
"*.html",
"app.ico",
"node_modules/**/*"
],
"mac": {
"icon": "app.ico",
"target": [
"dmg",
"zip"
]
},
"win": {
"icon": "app.ico",
"target": [
"zip"
],
"extraResources": "./libs/**/*"
},
"nsis": {
"oneClick": false,
"allowElevation": true,
"allowToChangeInstallationDirectory": true,
"installerIcon": "app.ico",
"uninstallerIcon": "app.ico",
"installerHeaderIcon": "app.ico",
"createDesktopShortcut": true,
"createStartMenuShortcut": true,
"license": "LICENSE.txt"
}
},
"devDependencies": {
"electron": "^11.1.1",
"electron-builder": "22.9.1"
}
}
其中最重要的配置就是 "extraResources": "./libs/**/*"
这样的话我们的所有配置就算完成了。
注意NodeJS的版本要在14以上
代码语言:javascript复制nvm install 14.17.1
nvm use 14.17.1
依赖版本号
代码语言:javascript复制"electron-builder": "22.9.1"
"electron-builder": "~22.9.1"
"electron-builder": "^22.9.1"
其中
- 前面不带符号则版本号的3个数字都匹配
~
则版本号的前2个数字匹配^
则版本号的第一个数字匹配
建议
使用
~
版本号