-
Notifications
You must be signed in to change notification settings - Fork 1k
/
NodeJsServer_asr.aliyun.short.js
282 lines (249 loc) · 9.09 KB
/
NodeJsServer_asr.aliyun.short.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*******************************************
ASR 阿里云-智能语音交互-一句话识别 生成token的api接口,本地测试http后端服务
https://github.com/xiangyuecn/Recorder
【运行】直接在当前文件夹内用命令行执行命令,即可运行:
node NodeJsServer_asr.aliyun.short.js
【运行前先修改以下配置必填项】:
*******************************************/
global.Config={
//请到阿里云开通 一句话识别 服务(可试用一段时间,正式使用时应当开通商用版,很便宜),得到AccessKey、Secret,参考:https://help.aliyun.com/document_detail/324194.html
AccessKey:"" //【必填】根据文档创建RAM用户,分配好权限,生成访问密钥
,Secret:"" //【必填】
//请到阿里云智能语音交互控制台创建相应的语音识别项目,每个项目可以设置一种语言模型,要支持多种语言就创建多个项目,然后填写每个项目对应的Appkey到这里,前端调用本接口的时候传入lang参数来获取到对应的Appkey
,Langs:{
"普通话":"" //【必填】至少创建一个普通话语言模型的项目用于测试,填对应的Appkey
,"粤语":""
,"英语":""
,"日语":""
//...... 按需提供语言对应的项目Appkey
}
};
global.Port=9527;//http服务端口
global.SSL={
CertFile:"" //cert.pem 证书路径,不为空将开启https
,KeyFile:"" //cert.key 密钥路径
};
//如果提供了这个本地文件,就加载过来,可覆盖global下挂载的对象
var fs = require("fs");
var localFile="NodeJsServer_asr.aliyun.short__local.js";
fs.existsSync(localFile) && require("./"+localFile);
var GetLangKeys=function(){
var keys=[];
for(var k in Config.Langs) if(Config.Langs[k])keys.push(k);
return keys;
};
/***************一句话识别Token生成***************/
var GetAsrToken=async function(ctx, args){
var lang=args.lang;//unsafe
var rtv={};
ctx.setValue(rtv);
var errTag="[GetAsrToken]";
if(!Config.AccessKey || !Config.Secret){
return ctx.error(errTag+"未配置AccessKey");
}
//根据前端需要的语言获得appkey,一种语言模型对应一个项目
var appkey=Config.Langs[lang];
if(!appkey){
var errMsg="lang["+lang+"]未配置Appkey";
if(!lang) errMsg="请求需要提供lang参数";
return ctx.error(errTag+errMsg+",已配置的可用lang值"+JSON.stringify(GetLangKeys()));
}
rtv.appkey=appkey;
//获得Access Token,Token有效期内可以重复使用,实际使用时应当全局共享,并做好并发控制
var cache=AccessToken_RedisCache;//假装读取Redis缓存数据
if(!cache || cache.ExpireTime-Date.now()/1000<60){//缓存无效
/*RedisLock()*/{ //假装加锁,得到锁的允许更新Token
cache=AccessToken_RedisCache;//假装得到锁后,二次确认是否抢占到更新机会
if(!cache || cache.ExpireTime-Date.now()/1000<60){
cache=await NewAccessToken(ctx);
if(ctx.isError()){
return ctx.error(errTag+ctx.getMsg());
}
console.log("NewAccessToken", cache);
AccessToken_RedisCache=cache;//假装写入Redis缓存
}
}
}
rtv.token=cache.Id;
ctx.endLog=function(){
console.log(" \x1B[32m"+lang+"token:"+JSON.stringify(rtv)+"\x1B[0m"
+"\n 此token可重复使用,有效期到:"+new Date(cache.ExpireTime*1000).toLocaleString());
};
return ctx;
};
var AccessToken_RedisCache=null;
//调用阿里云接口,获得新的Access Token,文档:https://help.aliyun.com/document_detail/113251.html
var NewAccessToken=async function(ctx){
var params={
AccessKeyId:Config.AccessKey
,Action:"CreateToken"
,Version:"2019-02-28"
,Format:"JSON"
,RegionId:"cn-shanghai"
,Timestamp:JSON.stringify(new Date()).replace(/"/g,"")
,SignatureMethod:"HMAC-SHA1"
,SignatureVersion:"1.0"
,SignatureNonce:RandomHEX(32).replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/,"$1-$2-$3-$4-$5")
};
var arr=[];
for(var k in params){
arr.push(urlEncodeX(k)+"="+urlEncodeX(params[k]));
};
arr.sort();
params=arr.join("&");
var signStr="GET&"+urlEncodeX("/")+"&"+urlEncodeX(params);
var sign=HMAC('sha1',Config.Secret+"&",signStr,'base64');
params="Signature="+urlEncodeX(sign)+"&"+params;
//直接发起get请求
var load=LoadRequest("http://nls-meta.cn-shanghai.aliyuncs.com/?"+params);
await load.getString(false);
var respTxt=load.response.data;
var data={};
try{
data=JSON.parse(respTxt);
}catch(e){}
if(!data.Token){
ctx.error("获取Token失败["+data.Code+"]:"+data.Message);
return null;
};
return data.Token;
};
//一些函数
var LoadRequest=require("./lib.load.js");
var Crypto=require('crypto');
var urlEncodeX=function(val){
return encodeURIComponent(val).replace(/[^\w\-\.\~\%]/g,function(a){
return "%"+("0"+a.charCodeAt(0).toString(16)).substr(-2).toUpperCase();
});
};
var RandomHEX=function(len){
var s=[];
for(var i=0,r;i<len;i++){
r=Math.floor(Math.random()*16);
if(r<10){
s.push(String.fromCharCode(r+48));
}else{
s.push(String.fromCharCode(r-10+97));
};
};
return s.join("");
};
var HMAC=function(hash,key,data,resType){
var hmac = Crypto.createHmac(hash, key);
hmac.update(data);
return hmac.digest(resType===null?null:(resType||'hex'));
};
/***************提供接口***************/
var Api={}; //async method_path0(ctx,args)
//内容回显
Api.GET_=Api.GET_echo=async function(ctx,args){
ctx.setValue('<h1>阿里云一句话识别TokenApi服务正在运行...</h1>','html');
};
Api.POST_=Api.POST_echo=async function(ctx,args){
ctx.setValue({url:ctx.req.url, method:ctx.req.method, headers:ctx.req.headers, args:args});
};
//token接口
Api.GET_token=Api.POST_token=async function(ctx,args){
await GetAsrToken(ctx,args);
};
/***开启http服务***/
var http = require('http');
var https = require('https');
var querystring = require('querystring');
var reqProcess=function(req, rep) {
var ctx={
req:req,rep:req
,data:{c:0,m:"",v:""}
,status:200,setCT:false,contentType:"text/json; charset=utf-8"
};
ctx.setValue=function(val,contentType){
if(contentType!=null){
ctx.setCT=true;
ctx.contentType=!contentType||contentType=="html"?"text/html; charset=utf-8":contentType;
};
ctx.data.v=val;
};
ctx.error=function(msg){
ctx.data.c=1;
ctx.data.m=msg;
return ctx;
};
ctx.isError=function(){
return ctx.data.c!=0;
};
ctx.getMsg=function(){
return ctx.data.m;
};
ctx.endLog=function(){};
var post = Buffer.alloc(0);
req.on('data', function(chunk){
post = Buffer.concat([post, chunk], post.length+chunk.length);
});
req.on('end', function(){
(async function(){
if(req.method!="GET" && req.method!="POST"){
ctx.setValue("Method: "+req.method, "html");
}else try{
//解析url查询参数 和 post表单
var params = querystring.parse((/\?(.+)/.exec(req.url)||[])[1]||"");
params = Object.assign(params, querystring.parse(post.toString()));
var path0=(/^\/([^\/\?]+)/.exec(req.url)||[])[1]||"";
var fn=Api[req.method+"_"+path0]
if(fn){
await fn(ctx, params);
}else{
ctx.status=404;
ctx.error(req.method+"路径不存在:"+req.url);
};
}catch(e){
ctx.error("执行出错:"+e.stack);
};
if(!ctx.setCT){
try{
var sendData=JSON.stringify(ctx.data);
}catch(e){
sendData=JSON.stringify({c:1,m:"返回数据失败:"+e.stack});
}
}else{
var sendData=ctx.data.v;
}
rep.writeHead(ctx.status, {
'Content-Type': ctx.contentType
, 'Access-Control-Allow-Origin':'*'
});
rep.end(sendData);
console.log("["+new Date().toLocaleString()+"]:"+Port+" "+req.method+" "+ctx.status+" "+Buffer.byteLength(sendData)+" "+post.length+" "+req.url);
if(ctx.isError()){
console.log(" \x1B[31m"+ctx.getMsg()+"\x1B[0m");
}
ctx.endLog();
})();
});
};
var UrlProtocol="http";
var UrlPort=Port==80?"":":"+Port;
if(SSL.CertFile){
UrlProtocol="https";
UrlPort=Port==443?"":":"+Port;
}
console.log("正在创建"+UrlProtocol+"服务(port:"+Port+")...");
if(SSL.CertFile){
https.createServer({
cert: fs.readFileSync(SSL.CertFile)
,key: fs.readFileSync(SSL.KeyFile)
}, reqProcess).listen(Port);
}else{
http.createServer(reqProcess).listen(Port);
}
console.log("\x1B[32m%s\x1B[0m",UrlProtocol.toUpperCase()+"服务正在运行...");
console.log(`
请在语音识别测试页面填写接口地址: \x1B[33m
${UrlProtocol}://127.0.0.1${UrlPort}/token
${UrlProtocol}://localhost${UrlPort}/token
${UrlProtocol}://你的局域网IP${UrlPort}/token
${UrlProtocol}://你的域名${UrlPort}/token
\x1B[0m
如果你的测试页面\x1B[35m无法访问http地址\x1B[0m或\x1B[35m存在跨域问题\x1B[0m时,请在本电脑上直接访问 \x1B[33m${UrlProtocol}://127.0.0.1${UrlPort}/token?lang=你需要lang语言\x1B[0m
然后手动复制 \x1B[33m{appkey:'...',token:'...'}\x1B[0m json到页面中使用,复制的token可以重复使用,本控制台中会显示有效期
已配置的可用lang语言\x1B[33m${JSON.stringify(GetLangKeys())}\x1B[0m
`);