zl程序教程

您现在的位置是:首页 >  后端

当前栏目

用nodejs访问ActiveX对象,以操作Access数据库为例。

Nodejs数据库对象 操作 访问 access 为例 ActiveX
2023-06-13 09:14:31 时间
起因
有人提问“如果用nodejs访问sqlserver?”
找了找资料,发现有两类解决方法,使用第三方nodejs插件:https://github.com/orenmazor/node-tds、使用ADODB.ConnectionActiveX对象。
参考:
http://stackoverflow.com/questions/857670/how-to-connect-to-sql-server-database-from-javascript
http://stackoverflow.com/questions/4728385/connecting-to-a-remote-microsoft-sql-server-from-node-js
如果用ActiveX那么在Windows下nodejs将会无所不能,类似写asp。那它们怎么通信?得动手试试
经过
思路
用nodejs通过cscript.exe(windows脚本进程)间接访问ActiveX
cscript能解析jscript和vbscript两种脚本,无疑为方便维护选jscript开发。
参考:http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/cscript_overview.mspx?mfr=true
需解决的问题
1、跨进程通信
新版的nodejs里增加了对子进程的操作,跨进程通信不是问题。
http://nodejs.org/docs/latest/api/all.html#child_Processes
复制代码代码如下:

varutil=require("util"),
exec=require("child_process").exec,
child;


child=exec("cat*.jsbad_file|wc-l",
function(error,stdout,stderr){
console.log("stdout:"+stdout);
console.log("stderr:"+stderr);
if(error!==null){
console.log("execerror:"+error);
}
});

如例我们可以拿到控制台的输出内容stdout!


2、数据库访问相关ActiveX,ADODB.Connection
参考:http://msdn.microsoft.com/en-us/library/windows/desktop/aa746471%28v=vs.85%29.aspx
复制代码代码如下:

varconnection=newActiveXObject("ADODB.Connection");
varresult="ok";
try{
connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;DataSource="+params.accessfile);
connection.Execute(params.sql);
}catch(ex){
result=ex.message;
}
return{
result:result
};

connection.Open(connectionString),链接字符串参数可以设置访问sqlserver。
参考:http://www.connectionstrings.com/sql-server-2005
3、为方便维护,特别将cscript和nodejs的脚本合并,用typeofexports判断当前运行环境。
4、字符编码cscript代码使用ascii编码
非ascii码字符进行“\uHHHH”Unicode编码。
5、命令行字符需转义,双引号、百分号在命令行有特殊意义。
参数传递使用base64编码,避免冲突
cscript环境MSXML2.DOMDocument可以做base64编解码
复制代码代码如下:
functionbase64Decode(base64){
varxmldom=newActiveXObject("MSXML2.DOMDocument");
varadostream=newActiveXObject("ADODB.Stream");
vartemp=xmldom.createElement("temp");
temp.dataType="bin.base64";
temp.text=base64;


adostream.Charset="utf-8";
adostream.Type=1;//1=adTypeBinary2=adTypeText
adostream.Open();
adostream.Write(temp.nodeTypedValue);
adostream.Position=0;
adostream.Type=2;//1=adTypeBinary2=adTypeText
varresult=adostream.ReadText(-1);//-1=adReadAll
adostream.Close();
adostream=null;
xmldom=null;
returnresult;
}


总结
调用流程
1、创建子进程,传递经过编码的参数;
2、子进程处理完毕将数据JSON格式化输出到控制台;(子进程自动结束)
3、读取控制台的数据,执行回调函数。


优势
1、使nodejs拥有访问ActiveX对象的能力;
2、实现简单,开发维护方便。


劣势
1、只能运行在Windows平台;
2、数据编解码会消耗更多cpu;
3、每次调用需要创建一个子进程重新连接。(可改进)
总结
1、具有一定实用性;
2、跨进程通信性能可继续探索。
模块代码:
复制代码代码如下:
varAccess={
create:function(params){
varfso=newActiveXObject("Scripting.FileSystemObject");
varresult="ok";
if(!fso.FileExists(params.accessfile)){
varadoxcatalog=newActiveXObject("ADOX.Catalog");
try{
adoxcatalog.Create("Provider=Microsoft.Jet.OLEDB.4.0;DataSource="+params.accessfile);
}catch(ex){
result=ex.message;
return;
}
adoxcatalog=null;
}else{
result="exists";
}
return{
result:result
};
},
existsTable:function(params){
varconnection=newActiveXObject("ADODB.Connection");
varresult="ok",exists=false;
try{
connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;DataSource="+params.accessfile);
varrecordset=connection.OpenSchema(20/*adSchemaTables*/);
recordset.MoveFirst();
while(!recordset.EOF){
if(recordset("TABLE_TYPE")=="TABLE"&&recordset("TABLE_NAME")==params.tablename){
exists=true;
break;
}
recordset.MoveNext();
}
recordset.Close();
recordset=null;
}catch(ex){
result=ex.message;
}
return{
"result":result,
"exists":exists
};
},
execute:function(params){
varconnection=newActiveXObject("ADODB.Connection");
varresult="ok";
try{
connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;DataSource="+params.accessfile);
connection.Execute(params.sql);
}catch(ex){
result=ex.message;
}
return{
result:result
};
},
query:function(params){
varconnection=newActiveXObject("ADODB.Connection");
varresult="ok",records=[];
try{
connection.Open("Provider=Microsoft.Jet.OLEDB.4.0;DataSource="+params.accessfile);
varrecordset=newActiveXObject("ADODB.Recordset");
recordset.Open(params.sql,connection);
varfields=[];
varenumer=newEnumerator(recordset.Fields);
for(;!enumer.atEnd();enumer.moveNext()){
fields.push(enumer.item().name);
}
recordset.MoveFirst();
while(!recordset.EOF){
varitem={};
for(vari=0;i<fields.length;i++){
varfieldname=fields[i];
item[fieldname]=recordset(fieldname).value;
}
records.push(item);
recordset.MoveNext();
}
recordset.Close();
recordset=null;
}catch(ex){
result=ex.message;
}
return{
result:result,
records:records
};
}
};
if(/^u/.test(typeofexports)){//cscript
voidfunction(){
//fromhttp://tangram.baidu.com/api.html#baidu.json
varJSON={
stringify:(function(){
/**
*字符串处理时需要转义的字符表
*@private
*/
varescapeMap={
"\b":"\\b",
"\t":"\\t",
"\n":"\\n",
"\f":"\\f",
"\r":"\\r",
""":"\\"",
"\\":"\\\\"
};
/**
*字符串序列化
*@private
*/
functionencodeString(source){
if(/["\\\x00-\x1f]/.test(source)){
source=source.replace(
/["\\\x00-\x1f]/g,
function(match){
varc=escapeMap[match];
if(c){
returnc;
}
c=match.charCodeAt();
return"\\u00"
+Math.floor(c/16).toString(16)
+(c%16).toString(16);
});
}
return"""+source+""";
}
/**
*数组序列化
*@private
*/
functionencodeArray(source){
varresult=["["],
l=source.length,
preComma,i,item;
for(i=0;i<l;i++){
item=source[i];
switch(typeofitem){
case"undefined":
case"function":
case"unknown":
break;
default:
if(preComma){
result.push(",");
}
result.push(JSON.stringify(item));
preComma=1;
}
}
result.push("]");
returnresult.join("");
}
/**
*处理日期序列化时的补零
*@private
*/
functionpad(source){
returnsource<10?"0"+source:source;
}
/**
*日期序列化
*@private
*/
functionencodeDate(source){
return"""+source.getFullYear()+"-"
+pad(source.getMonth()+1)+"-"
+pad(source.getDate())+"T"
+pad(source.getHours())+":"
+pad(source.getMinutes())+":"
+pad(source.getSeconds())+""";
}
returnfunction(value){
switch(typeofvalue){
case"undefined":
return"undefined";
case"number":
returnisFinite(value)?String(value):"null";
case"string":
returnencodeString(value).replace(/[^\x00-\xff]/g,function(all){
return"\\u"+(0x10000+all.charCodeAt(0)).toString(16).substring(1);
});
case"boolean":
returnString(value);
default:
if(value===null){
return"null";
}
if(valueinstanceofArray){
returnencodeArray(value);
}
if(valueinstanceofDate){
returnencodeDate(value);
}
varresult=["{"],
encode=JSON.stringify,
preComma,
item;
for(varkeyinvalue){
if(Object.prototype.hasOwnProperty.call(value,key)){
item=value[key];
switch(typeofitem){
case"undefined":
case"unknown":
case"function":
break;
default:
if(preComma){
result.push(",");
}
preComma=1;
result.push(encode(key)+":"+encode(item));
}
}
}
result.push("}");
returnresult.join("");
}
};
})(),
parse:function(data){
return(newFunction("return("+data+")"))();
}
}
//http://blog.csdn.net/cuixiping/article/details/409468
functionbase64Decode(base64){
varxmldom=newActiveXObject("MSXML2.DOMDocument");
varadostream=newActiveXObject("ADODB.Stream");
vartemp=xmldom.createElement("temp");
temp.dataType="bin.base64";
temp.text=base64;
adostream.Charset="utf-8";
adostream.Type=1;//1=adTypeBinary2=adTypeText
adostream.Open();
adostream.Write(temp.nodeTypedValue);
adostream.Position=0;
adostream.Type=2;//1=adTypeBinary2=adTypeText
varresult=adostream.ReadText(-1);//-1=adReadAll
adostream.Close();
adostream=null;
xmldom=null;
returnresult;
}
WScript.StdOut.Write("<json>");
varmethod=Access[WScript.Arguments(0)];
varresult=null;
if(method){
result=method(JSON.parse(base64Decode(WScript.Arguments(1))));
}
WScript.StdOut.Write(JSON.stringify(result));
WScript.StdOut.Write("</json>");
}();
}else{//nodejs
voidfunction(){
functionjson4stdout(stdout){
if(!stdout)return;
varresult=null;
String(stdout).replace(/<json>([\s\S]+)<\/json>/,function(){
result=JSON.parse(arguments[1]);
});
returnresult;
}
varutil=require("util"),exec=require("child_process").exec;
for(varnameinAccess){
exports[name]=(function(funcname){
returnfunction(params,callback){
console.log([funcname,params]);
exec(
util.format(
"cscript.exe/e:jscript"%s"%s"%s"",__filename,
funcname,
(newBuffer(JSON.stringify(params))).toString("base64")
),
function(error,stdout,stderr){
if(error!=null){
console.log("execerror:"+error);
return;
}
console.log("stdout:"+stdout);
callback&&callback(json4stdout(stdout));
}
);
}
})(name);
}
}();
}

调用代码:
复制代码代码如下:
varaccess=require("./access.js");
varutil=require("util");
varaccessfile="demo.mdb";
access.create({accessfile:accessfile},function(data){
console.log(data);
});
access.existsTable({accessfile:accessfile,tablename:"demo"},function(data){
if(data.result=="ok"&&!data.exists){
access.execute({
accessfile:"demo.mdb",
sql:"CREATETABLEdemo(idCounterPrimarykey,dataText(100))"
});
}
});
access.execute({
accessfile:"demo.mdb",
sql:util.format("INSERTINTOdemo(data)VALUES("zswang路过!%s")",+newDate)
},function(data){
console.log(data);
});
access.query({
accessfile:"demo.mdb",
sql:"SELECT*FROMdemo"
},function(data){
console.log(data);
});

最新代码:http://code.google.com/p/nodejs-demo/source/browse/#svn%2Ftrunk%2Fdatabase