我有一个相当复杂的设置,它需要web浏览器本地存储有计算机的名称填充,以便应用程序正常工作。为了做到这一点,我从配置文件中读取:
kiosk-name: Mort
当我启动node.js web服务器时,我读取配置文件:
var filesys = require('fs');
var os = require('os');
filesys.readFile(project_path + '/kiosk.cfg', 'utf8', function(err, data) {
var kioskname;
if (err) {
//console.log(err);
kioskname = os.hostname();
} else {
var configArray = data.split(':');
if('' != configArray[1]) {
kioskname = configArray[1];
} else {
kioskname = os.hostname();
}
}
});
所有这些都按照设计工作,当配置文件未填充时,使用计算机的os.hostname()
作为默认值。
客户端具有一个基本页面(index.html),它将默认页面(default.html)加载到iframe
中。基于websocket消息传递系统,默认页面被来自远程IP的另一个页面所取代。在旧版本的系统中(在实现配置文件之前),我们可以使用以下代码设置本地存储元素:
var win = document.getElementsByTagName('iframe')[0].contentWindow;
win.postMessage(JSON.stringify({key: 'kiosk-name', data: kioskName}), "*");
我们在收到websocket消息时识别iframe,然后发送包含JSON字符串的post消息来设置本地存储元素。在本例中,kioskName
是包含硬编码值的变量。
现在我们希望从配置文件中读取值,我们需要一种方法将kioskname
传递到客户端JavaScript,以便我们可以在iframe
中设置本地存储元素。
我尝试将文件读取函数放在export
包装器中:
(function(exports){
// file reading code here
return kioskname;
})(typeof exports === 'undefined' ? this['kioskname']={} : exports);
我得到一个错误:
Uncaught ReferenceError: require is not defined
在export
函数中放置静态值(没有require
's允许导出功能正常工作,但不允许我读取需要os
和fs
模块的配置文件。
我如何获得从配置文件返回的值到我可以在客户端使用它来设置本地存储元素的地方?
这是一个创造性的解决方案,可能并不适用于所有情况,因为它涉及到在Node.js web服务器和客户端之间使用websocket。
Websocket设置发送到客户端(假设webserver在' node_server
':
var io = require('socket.io').listen(node_server); // 'attaches' socket.io to this web server
io.sockets.on('connection', function (socket) {
socket.emit('message', 'socket.io connected'); // output a connection message
// receive JSON message and send to the websocket
socket.on('message', function (data) {
var address = node_server.address();
var client = dgram.createSocket("udp4");
var message = new Buffer(data);
// out of the airlock!
client.send(message, 0, message.length, address.port, address.address, function(err, bytes) {
client.close();
});
});
});
读取配置文件,然后解析并发送消息到套接字(在服务器端完成):
filesys.readFile(project_path + '/kiosk.cfg', 'utf8', function(err, data) {
var kioskname;
if (err) {
//console.log(err);
kioskname = os.hostname();
} else {
var configArray = data.split(':');
if('' != configArray[1]) {
kioskname = configArray[1];
} else {
kioskname = os.hostname();
}
}
// create JSON string for transmission
KioskName = JSON.stringify({'config':{'kiosk-name': kioskname}});
var send_KioskName = setInterval(function(){ // could be a setTimeout for a one time send
io.sockets.emit('message', KioskName.toString()); // send config file data to browser via socket
}, 30000);
});
注意这个可以扩展为在需要时通过JSON向客户端发送多个数据块。要设置一个更详细的JSON对象,只需要进行几个小的编辑。
在客户端接收套接字消息(此代码由客户端加载),然后解析。结果对象被添加到此应用程序的名称空间中,使该对象在需要时可供多个脚本使用。
注意:你应该只对那些不会干扰你在脚本中创建或销毁的对象使用这种方法。
// make sure a string is JSON before using
function isJSON(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
// set up object 'array's
var kioskname = {};
// connect a socket to listen for incoming messages from the Big Giant Head
var socket = io();
socket.on('message', function (data) {
if(isJSON(data)) {
// parse the json
var json = $.parseJSON(data);
// determine how to use this JSON object, multiple objects may be sent
if('config' == Object.keys(json)[0]) {
/*
* send config data where needed - future proofed, just add cases
* and namespaced objects where required
*/
kioskname['name'] = json.config['kiosk-name'];
}
}
});
// attach objects to namespace
window.KIOSK.kioskname = kioskname;
现在可以使用该对象设置本地存储。在我们的例子中,我们向应用程序的服务器发布消息,它以localStorage.setItem()
响应:
发布消息:
var currentFrame = document.getElementsByTagName('iframe')[0].contentWindow;
currentFrame.postMessage(JSON.stringify({key: 'user-name', data: KIOSK.kioskname.name}), "*");
通过打开套接字并使用通过套接字传递的JSON字符串来填充命名空间对象,我们可以使用来自应用程序客户端配置文件的服务器端信息。