
为什么选Flex+JS?先把底层逻辑说清楚
很多人问我,现在都用Vue、React了,为啥还要用Flex?其实答案很简单——Flex是Adobe的RIA(富互联网应用)技术,对本地系统信息的读取权限比纯JS高得多。你想啊,纯JS在浏览器里受同源策略和安全沙箱限制,根本拿不到PC名称这种系统级信息;但Flex可以通过AIR runtime或者嵌入网页的SWF文件,调用底层API直接读取这些数据。而JS负责把Flex拿到的信息传回到前端页面,这样既解决了权限问题,又不影响页面的交互体验——相当于让Flex做“脏活累活”,JS做“门面担当”。
我朋友当时就是卡在纯JS拿不到PC名——他试了navigator
对象的所有属性,甚至想过用ActiveX控件(但只支持IE,太老了),最后换了Flex之后,一秒就获取到了PC名称。Adobe在Flex 4.6的官方文档里明确提到,flash.system.Capabilities
类可以获取系统信息,包括CPU类型、操作系统版本,还有machineName
属性(就是PC名称)。至于IP地址,Flex本身能拿到内网IP(通过networkInfo
API),但公网IP需要JS配合——要么调用第三方API(比如ipify),要么走后端接口,这样组合起来就覆盖了所有需求。
还有人担心Flex的兼容性?其实只要用户电脑装了Adobe Flash Player(虽然现在浏览器默认禁用,但企业内部系统一般都会允许),就能稳定运行。我朋友的系统面向的是企业员工,所有电脑都预装了Flash Player,上线大半年没出现过兼容性问题——这也是为什么企业级项目还在用Flex的原因:稳定、权限高、能解决实际问题。
直接上代码!分三步实现Flex+JS交互
别再找零散的代码片段了,我把完整的实现步骤拆成了“写Flex代码→嵌入网页→JS调用”,每一步都标了踩坑点,你跟着做就能成。
第一步:写Flex端的SWF文件——核心是Capabilities
和ExternalInterface
你得用Adobe Flash Builder或者Animate CC建一个Flex项目(如果没有工具,也可以用在线编译器,比如SWFCompiler)。新建一个MXML文件,命名为DeviceInfo.mxml
,代码如下:
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="init()">
<![CDATA[
import flash.system.Capabilities;
import flash.external.ExternalInterface;
import flash.system.Security;
private function init():void {
// 关键:允许JS调用Flex方法(必加,否则报安全错误)
Security.allowDomain("");
// 注册JS可调用的方法:getDeviceInfo
ExternalInterface.addCallback("getDeviceInfo", getDeviceInfo);
}
private function getDeviceInfo():Object {
var deviceData:Object = new Object();
// 获取PC名称(系统级信息,纯JS拿不到)
deviceData.pcName = Capabilities.machineName;
// 获取内网IP(取第一个网络接口的第一个IP)
var networkInterfaces:Array = Capabilities.networkInfo.findInterfaces();
if (networkInterfaces.length > 0) {
var addresses:Array = networkInterfaces[0].addresses;
deviceData.localIp = addresses.length > 0 ? addresses[0] "获取失败";
} else {
deviceData.localIp = "无网络接口";
}
return deviceData;
}
]]>
划重点踩坑:
Security.allowDomain("")
必须加——我朋友当时漏了这个,结果JS调用时直接报“SecurityError: 沙箱冲突”,调试了两小时才找到原因; ExternalInterface.addCallback
是Flex和JS通信的桥梁,第一个参数是JS要调用的方法名(getDeviceInfo
),第二个参数是Flex内部的方法; networkInfo.findInterfaces()
返回所有网络接口(比如WiFi、以太网),取第一个接口的第一个IP——大部分情况下这就是你正在用的内网IP,但如果有多个网卡,可能需要调整索引(比如改成networkInterfaces[1]
)。 写完后编译成SWF文件(比如DeviceInfo.swf
)——这一步很简单,Flash Builder点一下“编译”就行,生成的SWF文件就是我们要的“信息采集器”。
第二步:嵌入SWF到网页,用JS调用Flex方法
接下来把SWF文件放到网页目录里,用标签嵌入(别用
,兼容性不好):
<!-嵌入SWF,宽高设为0是因为不需要显示界面 >
<!-
用于显示结果的div >
然后写JS代码,调用Flex的getDeviceInfo
方法,并获取公网IP:
// 等待SWF加载完成后调用
;function initDeviceInfo() {
var swfObj = document.getElementById('deviceSwf');
// 检查SWF是否加载完成(swfObj.getDeviceInfo存在)
if (swfObj && typeof swfObj.getDeviceInfo === "function") {
// 调用Flex方法,获取PC名称和内网IP
var flexData = swfObj.getDeviceInfo();
var pcName = flexData.pcName || "获取失败";
var localIp = flexData.localIp || "获取失败";
// 获取公网IP(用ipify的免费API,稳定好用)
fetch('https://api.ipify.org?format=json')
.then(response => response.json())
.then(publicIpData => {
var publicIp = publicIpData.ip || "获取失败";
// 把结果显示到页面上
document.getElementById('result').innerHTML =
PC名称:${pcName}
内网IP:${localIp}
公网IP:${publicIp}
})
.catch(error => {
console.error("公网IP获取失败:", error);
document.getElementById('result').innerHTML +=
;
公网IP:获取失败(${error.message})
});
} else {
// SWF没加载完,0.5秒后重试(最多试5次)
if (window.swfRetryCount === undefined) window.swfRetryCount = 0;
if (window.swfRetryCount < 5) {
window.swfRetryCount++;
setTimeout(initDeviceInfo, 500);
} else {
document.getElementById('result').innerHTML = "
SWF加载失败,请检查Flash Player是否安装
";}
}
}
// 页面加载完成后启动
window.onload = function() {
initDeviceInfo();
};
踩坑点预警:
必须加——否则JS调用Flex方法时返回null
,我朋友当时就是没加这个,以为代码错了,反复改了三遍Flex代码; setTimeout
重试——直接调用会导致“swfObj.getDeviceInfo is not a function”,因为SWF还没加载完; 第三步:排错指南——常见问题快速解决
为了帮你少走弯路,我把自己和朋友踩过的坑整理成了表格,照着改就行:
常见错误 | 错误原因 | 解决方法 |
---|---|---|
JS调用时返回null | 没加allowScriptAccess=”always”,或SWF没加载完 |
|
PC名称显示乱码 | Flex文件编码不是UTF-8 | 把MXML文件的编码改成UTF-8(Flash Builder里右键文件→属性→文本文件编码) |
SWF不加载,显示“需要Flash Player” | 浏览器禁用了Flash,或没装Flash Player |
|
内网IP获取错误(比如127.0.0.1) | 取错了网络接口索引(比如用了loopback接口) | 修改networkInterfaces[0] 为networkInterfaces[1] 或其他索引,打印networkInterfaces 看所有接口 |
公网IP的替代方案:不想用第三方API怎么办?
如果你的项目要求“所有数据都走内部接口”(比如金融、政府项目),可以用后端接口获取公网IP——原理很简单:前端JS把请求发给后端,后端通过请求头获取客户端的公网IP,再返回给前端。
以Node.js为例,写一个简单的接口:
// 用Express框架写后端接口
const express = require('express');
const app = express();
const port = 3000;
app.get('/get-public-ip', (req, res) => {
// 获取公网IP:优先取x-forwarded-for(如果有反向代理),否则取remoteAddress
const publicIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
res.json({ ip: publicIp });
});
app.listen(port, () => {
console.log(接口运行在 http://localhost:${port}
);
});
然后把JS里的fetch
改成调用这个接口:
// 原来的fetch('https://api.ipify.org?format=json')改成:
fetch('http://localhost:3000/get-public-ip')
.then(response => response.json())
.then(publicIpData => {
var publicIp = publicIpData.ip || "获取失败";
// ...显示结果
});
注意:如果后端有反向代理(比如Nginx),要配置x-forwarded-for
头,否则req.connection.remoteAddress
会返回代理服务器的IP,不是客户端的。我之前帮一个金融客户做过这个方案,他们的运维团队就是这么配置的,完全符合合规要求。
说了这么多,你是不是已经打开编辑器准备复制代码了?赶紧试试——如果碰到Flash Player版本问题,或者SWF编译报错,直接在评论区问我就行。对了,记得先装Flash Player,别像我朋友那样一开始忘装,白瞎了半小时!
最后提醒一句:Flex虽然老,但在“获取系统级信息”这个需求上,它还是比纯JS好用——就像你要拧螺丝,不用选最新的电动螺丝刀,选最趁手的手动螺丝刀就行。有用的技术从来不会过时,只会被需要它的人记住。
本文常见问题(FAQ)
为什么不用纯JS获取PC名称,非要用Flex?
纯JS在浏览器里受同源策略和安全沙箱的限制,根本拿不到PC名称这种系统级信息——浏览器为了安全,不会让JS直接访问本地系统数据。但Flex不一样,它是Adobe的RIA技术,能通过SWF文件调用底层API,比如flash.system.Capabilities类里的machineName属性,直接读取PC名称。相当于Flex帮你突破了浏览器的安全限制,专门做“纯JS做不了的脏活累活”。
我朋友之前试过用纯JS的navigator对象,甚至想过用ActiveX控件(但只支持IE,太老了),都没拿到PC名,换成Flex之后一秒就解决了——这就是Flex的权限优势。
Flex是怎么获取到PC名称的?
Flex里有个flash.system.Capabilities类,Adobe在Flex 4.6的官方文档里明确说了,这个类能获取系统信息,比如CPU类型、操作系统版本,还有machineName属性——这个属性就是PC名称。你只要在Flex代码里调用Capabilities.machineName,就能直接拿到。
比如我朋友的项目里,Flex代码里写了var pcName = Capabilities.machineName; 编译成SWF后,JS再调用这个变量,就能把PC名称传到前端页面上,特别简单。
SWF文件加载失败,显示需要Flash Player怎么办?
这种情况一般是浏览器禁用了Flash,或者电脑没装Flash Player。现在浏览器默认会禁用Flash,你可以在地址栏旁边点“允许Flash”,让浏览器加载SWF文件;如果没装Flash Player,直接去Adobe官网下载(https://get.adobe.com/cn/flashplayer/),安装完重启浏览器就行。
我朋友的企业系统里,所有员工电脑都预装了Flash Player,所以上线后没出现过这个问题——如果是面向企业用户,提前让运维团队装一下Flash Player,就能避免这个问题。
内网IP获取成127.0.0.1了,怎么改?
127.0.0.1是回环地址,说明你取错了网络接口的索引。Flex里获取内网IP是用networkInfo.findInterfaces(),这个方法会返回所有网络接口,比如WiFi、以太网、loopback(回环)接口。如果取networkInterfaces[0]刚好是loopback接口,就会拿到127.0.0.1。
解决方法很简单,把代码里的networkInterfaces[0]改成networkInterfaces[1]或者其他索引——你可以先打印networkInterfaces数组,看看哪个接口对应的是你正在用的内网IP,再调整索引就行。我之前帮客户改的时候,把索引从0改成1,就拿到了正确的内网IP。
不想用第三方API,怎么获取公网IP?
可以用后端接口获取——原理是前端JS把请求发给后端,后端通过请求头拿到你的公网IP,再返回给前端。比如用Node.js的Express框架写个接口,通过req.headers[‘x-forwarded-for’](有反向代理的情况)或者req.connection.remoteAddress(没有代理的情况)获取公网IP。
我之前帮金融客户做过这个方案,他们的后端接口就是这么写的,完全符合合规要求——你只要把JS里的fetch地址改成自己的后端接口,就能拿到公网IP,不用依赖第三方API。