Flask + gunicorn + nginx 部署Web

服务端atool 发表了文章 • 0 个评论 • 508 次浏览 • 2016-04-22 12:46 • 来自相关话题

现在QA内部很多组都是用Flask做一些web开发,或者接口开发,然而很多应用可能直接使用Python 运行,这个存在很大的性能问题(之前做的AB测试数据)。下面介绍怎么使用Flask + gunicorn + nginx 部署一个Flask应用,实际上网上介绍这个的更多,但是大多偏于复杂,操作难度大。
 
一、准备好Flask应用
就是自己开发的Flask应用,可以使用Python跑起来即可,假设运行方式是python run_demo_flask,使用的端口是8080。
 
二、使用gunicorn
首先安装gunicorn,很简单,没有安装过的,直接:pip install gunicorn
pip install gevent使用gunicorn运行Flask:gunicorn --worker-class=gevent -w 4 -t 30 -b ip:8080 run_demo_flask:app注意:将ip换成你部署的服务器的IP地址,app为flask的实例对象名字。
 
三、使用nginx代理
 
可以将下面的配置放入到nginx的vhost目录,(名字为xxxx.conf,必须以conf结尾),然后重启nginx即可。
重启方式nginx -t # 测试下配置是否正确
service nginx restart # 如果正确就重启3.1 如果有域名,nginx 配置文件如下erver {
listen 80;
server_name xxx.163-inc.com;
client_max_body_size 128M;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}访问方式:http://xxx.163-inc.com

3.2 如果没有域名server {
listen 8081;
server_name ip;
client_max_body_size 128M;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
listen的端口换一个没有使用的端口,然后proxy_pass传入8080(flask_demo的端口)
访问方式:http://ip:8081
 
重启nginx即可完毕。 查看全部
现在QA内部很多组都是用Flask做一些web开发,或者接口开发,然而很多应用可能直接使用Python 运行,这个存在很大的性能问题(之前做的AB测试数据)。下面介绍怎么使用Flask + gunicorn + nginx 部署一个Flask应用,实际上网上介绍这个的更多,但是大多偏于复杂,操作难度大。
 
一、准备好Flask应用
就是自己开发的Flask应用,可以使用Python跑起来即可,假设运行方式是python run_demo_flask,使用的端口是8080。
 
二、使用gunicorn
首先安装gunicorn,很简单,没有安装过的,直接:
pip install gunicorn
pip install gevent
使用gunicorn运行Flask:
gunicorn --worker-class=gevent -w 4 -t 30 -b ip:8080 run_demo_flask:app
注意:将ip换成你部署的服务器的IP地址,app为flask的实例对象名字。
 
三、使用nginx代理
 
可以将下面的配置放入到nginx的vhost目录,(名字为xxxx.conf,必须以conf结尾),然后重启nginx即可。
重启方式
nginx -t  # 测试下配置是否正确
service nginx restart # 如果正确就重启
3.1 如果有域名,nginx 配置文件如下
erver {
listen 80;
server_name xxx.163-inc.com;
client_max_body_size 128M;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
访问方式:http://xxx.163-inc.com

3.2 如果没有域名
server {
listen 8081;
server_name ip;
client_max_body_size 128M;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_redirect off;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

listen的端口换一个没有使用的端口,然后proxy_pass传入8080(flask_demo的端口)
访问方式:http://ip:8081
 
重启nginx即可完毕。

Python minidom去除xml文本中空行、空格、tab,美化xml显示样式

编程语言atool 发表了文章 • 0 个评论 • 2084 次浏览 • 2016-04-22 12:37 • 来自相关话题

使用Python minidom的库函数进行xml文件或者文本的增加借点、删除借点、增加属性、删除属性、合并xml文件或者文本等这些操作的时候,往往多多出很多的空行,使用toxml()方法返回字符串保存到文件之后,空行依然存在,非常不美观,下面使用python介绍怎么去除xml文本中空行、空格、tab,美化xml显示样式,代码如下:
@classmethod
def _beautifulFormat(self, xmlDomObject):
'''美化xml格式
'''
if xmlDomObject:
#优化格式显示
xmlStr = xmlDomObject.toprettyxml(indent = '', newl = '', encoding = 'utf-8')
xmlStr = xmlStr.replace('\t', '').replace('\n', '')
xmlDomObject = minidom.parseString(xmlStr)
xmlStr = xmlDomObject.toprettyxml(indent = '\t', newl = '\n', encoding = 'utf-8')

return xmlStr
else:
return False这个是写到一个类中的静态方法,传入参数为minidom中的xml Object对象,返回的是格式化美化之后的xml文本。

大致思路是首先去掉xml中的所有空行、tab,然后在用minidom读入没有空行,tab的xml文本,然后再使用toprettyxml()方法格式化输出字符串。

自己在程序中使用,没有任何问题,如有问题请留言,如果帮助到你,请点点广告,你懂的~ 查看全部
使用Python minidom的库函数进行xml文件或者文本的增加借点、删除借点、增加属性、删除属性、合并xml文件或者文本等这些操作的时候,往往多多出很多的空行,使用toxml()方法返回字符串保存到文件之后,空行依然存在,非常不美观,下面使用python介绍怎么去除xml文本中空行、空格、tab,美化xml显示样式,代码如下:
 @classmethod
def _beautifulFormat(self, xmlDomObject):
'''美化xml格式
'''
if xmlDomObject:
#优化格式显示
xmlStr = xmlDomObject.toprettyxml(indent = '', newl = '', encoding = 'utf-8')
xmlStr = xmlStr.replace('\t', '').replace('\n', '')
xmlDomObject = minidom.parseString(xmlStr)
xmlStr = xmlDomObject.toprettyxml(indent = '\t', newl = '\n', encoding = 'utf-8')

return xmlStr
else:
return False
这个是写到一个类中的静态方法,传入参数为minidom中的xml Object对象,返回的是格式化美化之后的xml文本。

大致思路是首先去掉xml中的所有空行、tab,然后在用minidom读入没有空行,tab的xml文本,然后再使用toprettyxml()方法格式化输出字符串。

自己在程序中使用,没有任何问题,如有问题请留言,如果帮助到你,请点点广告,你懂的~

Java模拟登录微信公众平台,主动推送图文消息给用户

服务端atool 发表了文章 • 0 个评论 • 1500 次浏览 • 2016-04-22 11:37 • 来自相关话题

@2013-11-25,对博文进行说明,由于2013年10月底,微信公众平台界面完全改版,所以本博文的所有代码几乎全部不能使用,不过有网友对本博的代码进行修改并授权于我共享给大家,因此大家可以去看博文《【微信公众平台改版后】Java模拟登录微信平台,主动推送消息给用户》,本博文的思路,大家也可以看看进行参考。

很早之前,在博客中发表了关于使用java开发微信公众号接口的程序代码,详细见《微信公众号消息接口开发——Java Servlet开发》,例外利用java做了小黄鸡,小i机器人的微信聊天开发,所以之前很多人问我有关于java开发微信接口的东西~

除此之外,更重要的是:很多人还问我关于微信主动推送消息给用户的问题,我所给出的所有答案都是:微信官方没有给出主动推送的api,所有采用程序记录用户微信id,然后模拟微信接口的请求数据,去请求微信接口中设置的url,这些方法都是不可行的~

不过,要实现主动推送不是不可能的,最简单的一个思路就是使用java模拟登录微信公众平台,然后推送消息消息给用户,不过这种方式有推送限制。

由于问的人比较多,决定把代码放出来,代码还未整理,里面有解析数据可以用正则表达式替换也没做,有需要的可以自己在此基础上修改。如果你觉得有用,并帮助了你,感谢你对我的赞助和支持。
public boolean msgSend(MsgForm form, MsgType type) {
try {
if (!this.isLogin) {
this._login();
}
if (this.isLogin) {
form.setToken(this.token);
PostMethod post = new PostMethod(POST_MSG);
post.setRequestHeader(USER_AGENT_H, USER_AGENT);
post.setRequestHeader(REFERER_H, INDEX_URL);
post.setRequestHeader("Cookie", this.cookiestr);
NameValuePair[] params = null;
Part[] parts = null;
switch (type) {
case TEXT:

parts = new Part[]{
new StringPart("content", form.getContent(),
"UTF-8"),
new StringPart("type", form.getType()),
new StringPart("error", form.getError()),
new StringPart("needcomment", form.getNeedcomment()),
new StringPart("groupid", form.getGroupid()),
new StringPart("sex", form.getSex()),
new StringPart("country", form.getCountry()),
new StringPart("province", form.getProvince()),
new StringPart("city", form.getCity()),
new StringPart("token", form.getToken()),
new StringPart("ajax", form.getAjax()),
new StringPart("t", "ajax-response")};
break;
case IMAGE_TEXT:
parts = new Part[]{
new StringPart("content", form.getContent(),
"UTF-8"),
new StringPart("type", form.getType()),
new StringPart("error", form.getError()),
new StringPart("needcomment", form.getNeedcomment()),
new StringPart("groupid", form.getGroupid()),
new StringPart("sex", form.getSex()),
new StringPart("country", form.getCountry()),
new StringPart("province", form.getProvince()),
new StringPart("city", form.getCity()),
new StringPart("token", form.getToken()),
new StringPart("ajax", form.getAjax()),
new StringPart("t", "ajax-response")};
break;
default:
parts = new Part[]{
new StringPart("content", form.getContent(),
"UTF-8"),
new StringPart("type", form.getType()),
new StringPart("error", form.getError()),
new StringPart("needcomment", form.getNeedcomment()),
new StringPart("groupid", form.getGroupid()),
new StringPart("sex", form.getSex()),
new StringPart("country", form.getCountry()),
new StringPart("province", form.getProvince()),
new StringPart("city", form.getCity()),
new StringPart("token", form.getToken()),
new StringPart("ajax", form.getAjax()),
new StringPart("t", "ajax-response")};
break;
}
RequestEntity entity = new MultipartRequestEntity(parts,
post.getParams());
post.setRequestEntity(entity);
int status;
status = client.executeMethod(post);
if (status == HttpStatus.SC_OK) {
String text = post.getResponseBodyAsString();
try {
MsgJson ret = JSON.parseObject(text, MsgJson.class);
this.msgSendCode = ret.getRet();
switch (this.msgSendCode) {
case 0:
this.msgSendMsg = "发送成功";
return true;
case -2:
this.msgSendMsg = "参数错误,请仔细检查";
return false;
case 64004:
this.msgSendMsg = "今天的群发数量已到,无法群发";
return false;
case -20000:
this.msgSendMsg = "请求被禁止,请仔细检查token是否合法";
return false;
default:
this.msgSendMsg = "未知错误!";
return false;
}
} catch (Exception e) {
String info = "【群发信息失败】【解析json错误】" + e.getMessage()
+ "
【文本:】
" + text;
System.err.println(info);
log.debug(info);
log.info(info);
return false;
}
}
}
} catch (Exception e) {
String info = "【群发信息失败】" + e.getMessage();
System.err.println(info);
log.debug(info);
log.info(info);
return false;
}
return false;
} 查看全部
@2013-11-25,对博文进行说明,由于2013年10月底,微信公众平台界面完全改版,所以本博文的所有代码几乎全部不能使用,不过有网友对本博的代码进行修改并授权于我共享给大家,因此大家可以去看博文《【微信公众平台改版后】Java模拟登录微信平台,主动推送消息给用户》,本博文的思路,大家也可以看看进行参考。

很早之前,在博客中发表了关于使用java开发微信公众号接口的程序代码,详细见《微信公众号消息接口开发——Java Servlet开发》,例外利用java做了小黄鸡,小i机器人的微信聊天开发,所以之前很多人问我有关于java开发微信接口的东西~

除此之外,更重要的是:很多人还问我关于微信主动推送消息给用户的问题,我所给出的所有答案都是:微信官方没有给出主动推送的api,所有采用程序记录用户微信id,然后模拟微信接口的请求数据,去请求微信接口中设置的url,这些方法都是不可行的~

不过,要实现主动推送不是不可能的,最简单的一个思路就是使用java模拟登录微信公众平台,然后推送消息消息给用户,不过这种方式有推送限制。

由于问的人比较多,决定把代码放出来,代码还未整理,里面有解析数据可以用正则表达式替换也没做,有需要的可以自己在此基础上修改。如果你觉得有用,并帮助了你,感谢你对我的赞助和支持。
public boolean msgSend(MsgForm form, MsgType type) {
try {
if (!this.isLogin) {
this._login();
}
if (this.isLogin) {
form.setToken(this.token);
PostMethod post = new PostMethod(POST_MSG);
post.setRequestHeader(USER_AGENT_H, USER_AGENT);
post.setRequestHeader(REFERER_H, INDEX_URL);
post.setRequestHeader("Cookie", this.cookiestr);
NameValuePair[] params = null;
Part[] parts = null;
switch (type) {
case TEXT:

parts = new Part[]{
new StringPart("content", form.getContent(),
"UTF-8"),
new StringPart("type", form.getType()),
new StringPart("error", form.getError()),
new StringPart("needcomment", form.getNeedcomment()),
new StringPart("groupid", form.getGroupid()),
new StringPart("sex", form.getSex()),
new StringPart("country", form.getCountry()),
new StringPart("province", form.getProvince()),
new StringPart("city", form.getCity()),
new StringPart("token", form.getToken()),
new StringPart("ajax", form.getAjax()),
new StringPart("t", "ajax-response")};
break;
case IMAGE_TEXT:
parts = new Part[]{
new StringPart("content", form.getContent(),
"UTF-8"),
new StringPart("type", form.getType()),
new StringPart("error", form.getError()),
new StringPart("needcomment", form.getNeedcomment()),
new StringPart("groupid", form.getGroupid()),
new StringPart("sex", form.getSex()),
new StringPart("country", form.getCountry()),
new StringPart("province", form.getProvince()),
new StringPart("city", form.getCity()),
new StringPart("token", form.getToken()),
new StringPart("ajax", form.getAjax()),
new StringPart("t", "ajax-response")};
break;
default:
parts = new Part[]{
new StringPart("content", form.getContent(),
"UTF-8"),
new StringPart("type", form.getType()),
new StringPart("error", form.getError()),
new StringPart("needcomment", form.getNeedcomment()),
new StringPart("groupid", form.getGroupid()),
new StringPart("sex", form.getSex()),
new StringPart("country", form.getCountry()),
new StringPart("province", form.getProvince()),
new StringPart("city", form.getCity()),
new StringPart("token", form.getToken()),
new StringPart("ajax", form.getAjax()),
new StringPart("t", "ajax-response")};
break;
}
RequestEntity entity = new MultipartRequestEntity(parts,
post.getParams());
post.setRequestEntity(entity);
int status;
status = client.executeMethod(post);
if (status == HttpStatus.SC_OK) {
String text = post.getResponseBodyAsString();
try {
MsgJson ret = JSON.parseObject(text, MsgJson.class);
this.msgSendCode = ret.getRet();
switch (this.msgSendCode) {
case 0:
this.msgSendMsg = "发送成功";
return true;
case -2:
this.msgSendMsg = "参数错误,请仔细检查";
return false;
case 64004:
this.msgSendMsg = "今天的群发数量已到,无法群发";
return false;
case -20000:
this.msgSendMsg = "请求被禁止,请仔细检查token是否合法";
return false;
default:
this.msgSendMsg = "未知错误!";
return false;
}
} catch (Exception e) {
String info = "【群发信息失败】【解析json错误】" + e.getMessage()
+ "
【文本:】
" + text;
System.err.println(info);
log.debug(info);
log.info(info);
return false;
}
}
}
} catch (Exception e) {
String info = "【群发信息失败】" + e.getMessage();
System.err.println(info);
log.debug(info);
log.info(info);
return false;
}
return false;
}

逻辑地址,物理地址,页号,页大小,页内偏移之间的关系

开源硬件atool 发表了文章 • 0 个评论 • 1338 次浏览 • 2016-04-22 11:28 • 来自相关话题

一、相关计算方法
1.逻辑地址/页面大小=页号
2.逻辑地址 mod 页面大小=页内偏移
3.通过页号查找页表得到对应的物理区块
4.物理地址=物理区块x页大小+页内偏移

二、分页机制的作用
在分页机制是在段机制之后进行的,它进一步将线性地址转换为物理地址。 

80386使用4K字节大小的页,且每页的起始地址都被4K整除。因此,80386把4GB字节线性地址空间划分为1M个页面,采用了两级表结构。 

两级页表 

两级表的第一级表称为页目录,存储在一个4K字节的页中,页目录表共有1K个表项,每个表项为4个字节,线性地址最高的10位(22-31)用来产生第一级表索引,由该索引得到的表项中的内容定位了二级表中的一个表的地址,即下级页表所在的内存块号。 

第二级表称为页表,存储在一个4K字节页中,它包含了1K字节的表项,每个表项包含了一个页的物理地址。二级页表由线性地址的中间10位(12-21)位进行索引,定位页表表项,获得页的物理地址。页物理地址的高20位与线性地址的低12位形成最后的物理地址。

三、应用计算例子

1. 在一分页存储管理系统中,逻辑地址长度为16位,页面大小为4096字节,现有一逻辑地址为2F6AH,且第0, 1, 2页依次存放在物理块5, 10 ,11中,问相应的物理地址为多少?

    解:由题目所给给条件可知,本页式系统的逻辑地址结构为:

    页号P,页内位移W

    逻辑地址2F6AH的二进制表示如下:

    P                           W

    0010              111101101010

    由此可知逻辑地址2F6AH的页号为2,该页存放在第11号物理块中,用十六进制表示志号为B,所以物理地址为BF6AH.

2.(南开大学1994年试题)在采用页式存储管理的系统中,默作业J的逻辑地址空间为4页(每页2048字节),且已知该作业的页面映象表(即页表)如下:

    页号       块号

    0              2

    1           4

    2           6

    3           8

试借助地址变换图(即要求画出地址变换图)求出有效逻辑地址4865所对应的物理地址。

    解:在本题中,一页大小为2048字节,则逻辑得志4865的页号机页内位移:为:

    页号:        4865/2048=2

    页内位移      4865-2048x2=769

    然后,通过页表查知物理块号为6,将物理块号与逻辑地址中的页内位移拼接,形成物理地址,即:

            6*2048+769=13057 查看全部
一、相关计算方法
1.逻辑地址/页面大小=页号
2.逻辑地址 mod 页面大小=页内偏移
3.通过页号查找页表得到对应的物理区块
4.物理地址=物理区块x页大小+页内偏移

二、分页机制的作用
在分页机制是在段机制之后进行的,它进一步将线性地址转换为物理地址。 

80386使用4K字节大小的页,且每页的起始地址都被4K整除。因此,80386把4GB字节线性地址空间划分为1M个页面,采用了两级表结构。 

两级页表 

两级表的第一级表称为页目录,存储在一个4K字节的页中,页目录表共有1K个表项,每个表项为4个字节,线性地址最高的10位(22-31)用来产生第一级表索引,由该索引得到的表项中的内容定位了二级表中的一个表的地址,即下级页表所在的内存块号。 

第二级表称为页表,存储在一个4K字节页中,它包含了1K字节的表项,每个表项包含了一个页的物理地址。二级页表由线性地址的中间10位(12-21)位进行索引,定位页表表项,获得页的物理地址。页物理地址的高20位与线性地址的低12位形成最后的物理地址。

三、应用计算例子

1. 在一分页存储管理系统中,逻辑地址长度为16位,页面大小为4096字节,现有一逻辑地址为2F6AH,且第0, 1, 2页依次存放在物理块5, 10 ,11中,问相应的物理地址为多少?

    解:由题目所给给条件可知,本页式系统的逻辑地址结构为:

    页号P,页内位移W

    逻辑地址2F6AH的二进制表示如下:

    P                           W

    0010              111101101010

    由此可知逻辑地址2F6AH的页号为2,该页存放在第11号物理块中,用十六进制表示志号为B,所以物理地址为BF6AH.

2.(南开大学1994年试题)在采用页式存储管理的系统中,默作业J的逻辑地址空间为4页(每页2048字节),且已知该作业的页面映象表(即页表)如下:

    页号       块号

    0              2

    1           4

    2           6

    3           8

试借助地址变换图(即要求画出地址变换图)求出有效逻辑地址4865所对应的物理地址。

    解:在本题中,一页大小为2048字节,则逻辑得志4865的页号机页内位移:为:

    页号:        4865/2048=2

    页内位移      4865-2048x2=769

    然后,通过页表查知物理块号为6,将物理块号与逻辑地址中的页内位移拼接,形成物理地址,即:

            6*2048+769=13057

dom4j解析创建Xml:org.dom4j.DocumentException: 2字节的UTF-8序列的2无效

编程语言atool 发表了文章 • 0 个评论 • 2409 次浏览 • 2016-04-22 11:26 • 来自相关话题

最近刚刚做完Java版Dota改键软件KeyHelper的界面配置操作,配置信息保存到xml中,采用dom4j进行解析和创建,基本做的差不多,并导出为jar自己在使用测试。

在Eclipse中运行时没有任何异常和错误,但是导出jar包之后使用start java -jar keyHelper.jar运行的时候,报异常:org.dom4j.DocumentException: 2字节的UTF-8序列的2无效...后面是一大串错误stack信息。

搜索了一下,网上关于dom4j解析xml汉字乱码问题一大堆,其中解决办法基本没有,大都是在网上发发问题,但是没有人解决,只有一个提到说将xml的编码改成为GBK或者GB2312,这个测试了,基本上是可行的,但是一般的软件开发都是采用UTF-8,将配置换成其他编码,会导致软件界面上是乱码,所以这个解决办法其实有一点鸡肋。自己尝试过在创建保存xml的时候,利用构造的domcument,调用asXml()方法得到document的string字符串,然后采用java io利用utf-8编码写入文件中,但是到读取的时候依然会报错。

再三折腾之下,发现了一个既不用修改xml编码,又可以解决这个中文解析的异常,贴出我的解析的创建xml的代码。

一、解析Config.xml配置信息
private void loadFromXml() {
try {
AXReader reader = new SAXReader();
File file = new File(CONFIG_FILE);
Document document = reader.read(new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")));// 读取XML文件
Element xmlRoot = document.getRootElement();// 得到根节点
lastKeySet = xmlRoot.elementText("lastKeySet");
lastLocationX = Integer.valueOf(xmlRoot.elementText("lastLocationX"));
lastLocationY = Integer.valueOf(xmlRoot.elementText("lastLocationY"));
} catch (Exception e) {
e.printStackTrace();
lastKeySet = "";//上次选择的改键方案
lastLocationX = 500;//上次窗口位置
lastLocationY = 100;//上次窗口位置
}
}其中代码:Document document = reader.read(newBufferedReader(newInputStreamReader(newFileInputStream(file), "UTF-8")));是关键。

二、创建Config.xml配置信息
public void saveToXml() {
/** 建立document对象 */
Document document = DocumentHelper.createDocument();
/** 建立config根节点 */
Element configElement = document.addElement("config");
configElement.addElement("lastKeySet").setText(lastKeySet);
configElement.addElement("lastLocationX").setText(lastLocationX + "");
configElement.addElement("lastLocationY").setText(lastLocationY + "");
try{
/* 将document中的内容写入文件中 */
OutputFormat format = new OutputFormat();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream(new File(CONFIG_FILE)), format);
writer.write(document);
writer.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}其中代码:OutputFormat format = newOutputFormat();
format.setEncoding("UTF-8");
XMLWriter writer = newXMLWriter(newFileOutputStream(newFile(CONFIG_FILE)), format);是关键,请务必按照这个写法进行。具体为什么,我自己也不知道,唉~
Enjoy~ 查看全部
最近刚刚做完Java版Dota改键软件KeyHelper的界面配置操作,配置信息保存到xml中,采用dom4j进行解析和创建,基本做的差不多,并导出为jar自己在使用测试。

在Eclipse中运行时没有任何异常和错误,但是导出jar包之后使用start java -jar keyHelper.jar运行的时候,报异常:org.dom4j.DocumentException: 2字节的UTF-8序列的2无效...后面是一大串错误stack信息。

搜索了一下,网上关于dom4j解析xml汉字乱码问题一大堆,其中解决办法基本没有,大都是在网上发发问题,但是没有人解决,只有一个提到说将xml的编码改成为GBK或者GB2312,这个测试了,基本上是可行的,但是一般的软件开发都是采用UTF-8,将配置换成其他编码,会导致软件界面上是乱码,所以这个解决办法其实有一点鸡肋。自己尝试过在创建保存xml的时候,利用构造的domcument,调用asXml()方法得到document的string字符串,然后采用java io利用utf-8编码写入文件中,但是到读取的时候依然会报错。

再三折腾之下,发现了一个既不用修改xml编码,又可以解决这个中文解析的异常,贴出我的解析的创建xml的代码。

一、解析Config.xml配置信息
private void loadFromXml() {
try {
AXReader reader = new SAXReader();
File file = new File(CONFIG_FILE);
Document document = reader.read(new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")));// 读取XML文件
Element xmlRoot = document.getRootElement();// 得到根节点
lastKeySet = xmlRoot.elementText("lastKeySet");
lastLocationX = Integer.valueOf(xmlRoot.elementText("lastLocationX"));
lastLocationY = Integer.valueOf(xmlRoot.elementText("lastLocationY"));
} catch (Exception e) {
e.printStackTrace();
lastKeySet = "";//上次选择的改键方案
lastLocationX = 500;//上次窗口位置
lastLocationY = 100;//上次窗口位置
}
}
其中代码:Document document = reader.read(newBufferedReader(newInputStreamReader(newFileInputStream(file), "UTF-8")));是关键。

二、创建Config.xml配置信息
public void saveToXml() {
/** 建立document对象 */
Document document = DocumentHelper.createDocument();
/** 建立config根节点 */
Element configElement = document.addElement("config");
configElement.addElement("lastKeySet").setText(lastKeySet);
configElement.addElement("lastLocationX").setText(lastLocationX + "");
configElement.addElement("lastLocationY").setText(lastLocationY + "");
try{
/* 将document中的内容写入文件中 */
OutputFormat format = new OutputFormat();
format.setEncoding("UTF-8");
XMLWriter writer = new XMLWriter(new FileOutputStream(new File(CONFIG_FILE)), format);
writer.write(document);
writer.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
其中代码:
OutputFormat format = newOutputFormat();
format.setEncoding("UTF-8");
XMLWriter writer = newXMLWriter(newFileOutputStream(newFile(CONFIG_FILE)), format);
是关键,请务必按照这个写法进行。具体为什么,我自己也不知道,唉~
Enjoy~

PHP读ini文件内容到二维数组,写二维数组到ini文件

编程语言atool 发表了文章 • 0 个评论 • 921 次浏览 • 2016-04-22 11:24 • 来自相关话题

本文主要是说明PHP中二维数据和ini配置文件之间的转化和持久化问题,使用ini存储二维数组的目地是为了持久化保存一些很小的数据,使用数据库又会显得杀鸡使用了牛刀...

在Java语言中,对于一些经常使用的小量数据,一般会使用static类型的集合类来存储在内存中,然后,在php中,即使申明了static,页面一刷新,数据依然没有了,这样对于一些少量状态数据,要么使用数据库,要么使用memcache(需要另行配置软件,一般的php空间应该不支持吧),再就是使用shmop_open函数操作内存(似乎一般的主机也不会开发这个扩展,安全问题吧),或者使用其封装类SimpleSHM。

既然对于一般的主机来说,都不支持上述操作,那么上面的方法就不实用了,我想就只能使用文件进行存储了,php读写文件有四种方式,这个内容大家可以google“php读写文件有四种方式”,博文很多,而且内容都是统一的。我要讲的是其中的一个ini文件和多维数组的转化——php读写ini文件。

1.php读ini文件到二维数组
这个比较简单,使用函数:$userArray = parse_ini_file ( "user_state.ini", true );即可。
2.php写一/二维数组到ini文件
php之所以能够从ini文件中读出二维数组,是因为它有一个约定的数据格式,只要按照这个格式来进行写入内容即可,这个函数是php类库不提供的,但是下面的代码是本博客的公众微信号代码中一部分,用于保存用户的操作状态的代码,代码也不复杂。其中方法write_ini_file可以完成二维数组写入到ini文件中。
<?php
/**
* 记录用户状态
* @author http://50vip.com/
*
*/
class UserState {
public static function getUserState($userId) {
$userArray = parse_ini_file ( "user_state.ini", true );
if (count ( $userArray ) == 0) {
$userArray = array ();
$userArray ["$userId"] = array (
"state" => '0',
"info" => ''
);
UserState::write_ini_file($userArray, 'user_state.ini', true);
return array ("state" => '0', "info" => '');
}
else {
return $userArray["$userId"];
}
}
public static function setUserState($userId, $state, $info) {
$userArray = parse_ini_file ( "user_state.ini", true );
$userArray ["$userId"] = array (
"state" => $state,
"info" => $info
);
UserState::write_ini_file($userArray, 'user_state.ini', true);
}
/**
* 写ini配置文件
* @author http://50vip.com/
*/
private static function write_ini_file($assoc_arr, $path, $has_sections = true) {
$content = "";
if ($has_sections) {
foreach ( $assoc_arr as $key => $elem ) {
$content .= "[" . $key . "]\n";
foreach ( $elem as $key2 => $elem2 ) {
if (is_array ( $elem2 )) {
for($i = 0; $i < count ( $elem2 ); $i ++) {
$content .= $key2 . "[] = " . $elem2 [$i] . "\n";
}
} else if ($elem2 == "")
$content .= $key2 . " = \n";
else
$content .= $key2 . " = " . $elem2 . "\n";
}
}
} else {
foreach ( $assoc_arr as $key => $elem ) {
if (is_array ( $elem )) {
for($i = 0; $i < count ( $elem ); $i ++) {
$content .= $key2 . "[] = " . $elem [$i] . "\n";
}
} else if ($elem == "")
$content .= $key2 . " = \n";
else
$content .= $key2 . " = " . $elem . "\n";
}
}
if (! $handle = fopen ( $path, 'w' )) {
return false;
}
if (! fwrite ( $handle, $content )) {
return false;
}
fclose ( $handle );
return true;
}
}
?>上面的代码既包含的数组到ini的方法,也包含了它的使用方式。下面是ini配置文件的大致内容:
[oFCbmjmZD2pqUBrBB25XNjhyv6qQ]
state = 1
info =Enjoy~ 查看全部
本文主要是说明PHP中二维数据和ini配置文件之间的转化和持久化问题,使用ini存储二维数组的目地是为了持久化保存一些很小的数据,使用数据库又会显得杀鸡使用了牛刀...

在Java语言中,对于一些经常使用的小量数据,一般会使用static类型的集合类来存储在内存中,然后,在php中,即使申明了static,页面一刷新,数据依然没有了,这样对于一些少量状态数据,要么使用数据库,要么使用memcache(需要另行配置软件,一般的php空间应该不支持吧),再就是使用shmop_open函数操作内存(似乎一般的主机也不会开发这个扩展,安全问题吧),或者使用其封装类SimpleSHM。

既然对于一般的主机来说,都不支持上述操作,那么上面的方法就不实用了,我想就只能使用文件进行存储了,php读写文件有四种方式,这个内容大家可以google“php读写文件有四种方式”,博文很多,而且内容都是统一的。我要讲的是其中的一个ini文件和多维数组的转化——php读写ini文件。

1.php读ini文件到二维数组
这个比较简单,使用函数:
$userArray = parse_ini_file ( "user_state.ini", true );
即可。
2.php写一/二维数组到ini文件
php之所以能够从ini文件中读出二维数组,是因为它有一个约定的数据格式,只要按照这个格式来进行写入内容即可,这个函数是php类库不提供的,但是下面的代码是本博客的公众微信号代码中一部分,用于保存用户的操作状态的代码,代码也不复杂。其中方法write_ini_file可以完成二维数组写入到ini文件中。
<?php
/**
* 记录用户状态
* @author http://50vip.com/
*
*/
class UserState {
public static function getUserState($userId) {
$userArray = parse_ini_file ( "user_state.ini", true );
if (count ( $userArray ) == 0) {
$userArray = array ();
$userArray ["$userId"] = array (
"state" => '0',
"info" => ''
);
UserState::write_ini_file($userArray, 'user_state.ini', true);
return array ("state" => '0', "info" => '');
}
else {
return $userArray["$userId"];
}
}
public static function setUserState($userId, $state, $info) {
$userArray = parse_ini_file ( "user_state.ini", true );
$userArray ["$userId"] = array (
"state" => $state,
"info" => $info
);
UserState::write_ini_file($userArray, 'user_state.ini', true);
}
/**
* 写ini配置文件
* @author http://50vip.com/
*/
private static function write_ini_file($assoc_arr, $path, $has_sections = true) {
$content = "";
if ($has_sections) {
foreach ( $assoc_arr as $key => $elem ) {
$content .= "[" . $key . "]\n";
foreach ( $elem as $key2 => $elem2 ) {
if (is_array ( $elem2 )) {
for($i = 0; $i < count ( $elem2 ); $i ++) {
$content .= $key2 . "[] = " . $elem2 [$i] . "\n";
}
} else if ($elem2 == "")
$content .= $key2 . " = \n";
else
$content .= $key2 . " = " . $elem2 . "\n";
}
}
} else {
foreach ( $assoc_arr as $key => $elem ) {
if (is_array ( $elem )) {
for($i = 0; $i < count ( $elem ); $i ++) {
$content .= $key2 . "[] = " . $elem [$i] . "\n";
}
} else if ($elem == "")
$content .= $key2 . " = \n";
else
$content .= $key2 . " = " . $elem . "\n";
}
}
if (! $handle = fopen ( $path, 'w' )) {
return false;
}
if (! fwrite ( $handle, $content )) {
return false;
}
fclose ( $handle );
return true;
}
}
?>
上面的代码既包含的数组到ini的方法,也包含了它的使用方式。下面是ini配置文件的大致内容:
[oFCbmjmZD2pqUBrBB25XNjhyv6qQ]
state = 1
info =
Enjoy~

【错误】新浪SinaSAE 上传Java WAR包出现is not a javax.servlet.Filter

云计算atool 发表了文章 • 0 个评论 • 1292 次浏览 • 2016-04-22 11:22 • 来自相关话题

使用Java Web做了一个签到系统,使用的是Struts2 + ExtJs4 + BoneCP + Mysql。

准备部署到Sina SAE上面,之前部署微信公众号开发的的Java程序一点问题都没有,但是部署这个应用问题一堆。不过之前的应用使用的是Servlet,没有使用任何框架。

先说一个部署问题,大致是将war包上传之后,在SAE中的日志系统中的JVM日志有以下错误:

java.lang.IllegalStateException: class com.cxl.signin.filter.AuthFilter is not a javax.servlet.Filter

然后输入二级域名,发现前台显示也是报错的:

Error 404 - Not Found.No context on this server matched or handled this request.
Contexts known to this server are:...(后面是关于个人应用的一些乱鸡巴遭的东西)

最终在一个英文的讨论论坛找到了一个相关的回答:
My top-of-the-brain guess would be that you have two servlet JARs in your classpath, and jetty is using one but your WAR is using another.
Really weird error though, for sure.

大致意思是那哥们看到这个问题,第一感觉就是项目classpath里面有两个servlet jar包,服务器jetty使用一个,你的war包使用的另一个。

我想应该是sina的服务器classpath里面就有了servlet-api.jar包了,于是我删掉了web-INF/lib下面的这个包,上传,不报错,首页可以打开。我的二级域名为sign.sinaapp.com/login.jsp,大家测试。

另外还有一个需要注意的是:请不要用jre7的版本去编译你的项目...

除此之外,项目还有其他的问题,就是session问题,我Java开的是4个虚拟机,登陆的时候明明已经运行到登陆成功,session写入的那一步,然后该进入主界面了,但后面被登陆验证的过滤器拦截,得不到登陆时写入session,判定为没有登录,返回登陆页面.

有部署过类似Java项目到SAE上的同学帮个忙啊~~~ 查看全部
使用Java Web做了一个签到系统,使用的是Struts2 + ExtJs4 + BoneCP + Mysql。

准备部署到Sina SAE上面,之前部署微信公众号开发的的Java程序一点问题都没有,但是部署这个应用问题一堆。不过之前的应用使用的是Servlet,没有使用任何框架。

先说一个部署问题,大致是将war包上传之后,在SAE中的日志系统中的JVM日志有以下错误:

java.lang.IllegalStateException: class com.cxl.signin.filter.AuthFilter is not a javax.servlet.Filter

然后输入二级域名,发现前台显示也是报错的:

Error 404 - Not Found.No context on this server matched or handled this request.
Contexts known to this server are:...(后面是关于个人应用的一些乱鸡巴遭的东西)

最终在一个英文的讨论论坛找到了一个相关的回答:
My top-of-the-brain guess would be that you have two servlet JARs in your classpath, and jetty is using one but your WAR is using another.
Really weird error though, for sure.

大致意思是那哥们看到这个问题,第一感觉就是项目classpath里面有两个servlet jar包,服务器jetty使用一个,你的war包使用的另一个。

我想应该是sina的服务器classpath里面就有了servlet-api.jar包了,于是我删掉了web-INF/lib下面的这个包,上传,不报错,首页可以打开。我的二级域名为sign.sinaapp.com/login.jsp,大家测试。

另外还有一个需要注意的是:请不要用jre7的版本去编译你的项目...

除此之外,项目还有其他的问题,就是session问题,我Java开的是4个虚拟机,登陆的时候明明已经运行到登陆成功,session写入的那一步,然后该进入主界面了,但后面被登陆验证的过滤器拦截,得不到登陆时写入session,判定为没有登录,返回登陆页面.

有部署过类似Java项目到SAE上的同学帮个忙啊~~~

使用Java JMF技术编写视频播放器

图像/多媒体atool 发表了文章 • 0 个评论 • 803 次浏览 • 2016-04-22 11:21 • 来自相关话题

Java JMF意为Java媒体框架(JMF)。该核心框架支持不同媒体(如:音频输出和视频输出)间的时钟同步。它是一个标准的扩展框架,允许用户制作纯音频流和视频流。
下面的代码展示的是利用JMF和Java Swing编写的一个简单的视频播放器,因此运行这段代码,首先请安装JMF,安装地址,自己百度Google,完整代码如下:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.media.CannotRealizeException;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.EndOfMediaEvent;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoPlayerException;
import javax.media.Player;
import javax.media.PrefetchCompleteEvent;
import javax.media.RealizeCompleteEvent;
import javax.media.Time;
@SuppressWarnings({ "restriction", "unused" })
public class JMFSample implements ControllerListener {
public static void main(String[] args) {
JMFSample sp = new JMFSample();
sp.play();
}

private Player mediaPlayer;
private Frame f;
private Player player;
private Panel panel;
private Component visual;
private Component control = null;

public void play(){
f = new Frame("JMF Sample1");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
if(player != null) {
player.close();
}
System.exit(0);
}
});
f.setSize(500,400);
f.setVisible(true);
URL url = null;
try {
//准备一个要播放的视频文件的URL
url = new URL("file:/d:/2.mpg");
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
//通过调用Manager的createPlayer方法来创建一个Player的对象
//这个对象是媒体播放的核心控制对象
player = Manager.createPlayer(url);
} catch (NoPlayerException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
//对player对象注册监听器,能噶偶在相关事件发生的时候执行相关的动作
player.addControllerListener(this);

//让player对象进行相关的资源分配
player.realize();
}

private int videoWidth = 0;
private int videoHeight = 0;
private int controlHeight = 30;
private int insetWidth = 10;
private int insetHeight = 30;

//监听player的相关事件
public void controllerUpdate(ControllerEvent ce) {
if (ce instanceof RealizeCompleteEvent) {
//player实例化完成后进行player播放前预处理
player.prefetch();
} else if (ce instanceof PrefetchCompleteEvent) {
if (visual != null)
return;
//取得player中的播放视频的组件,并得到视频窗口的大小
//然后把视频窗口的组件添加到Frame窗口中,
if ((visual = player.getVisualComponent()) != null) {
Dimension size = visual.getPreferredSize();
videoWidth = size.width;
videoHeight = size.height;
f.add(visual);
} else {
videoWidth = 320;
}

//取得player中的视频播放控制条组件,并把该组件添加到Frame窗口中
if ((control = player.getControlPanelComponent()) != null) {
controlHeight = control.getPreferredSize().height;
f.add(control, BorderLayout.SOUTH);
}

//设定Frame窗口的大小,使得满足视频文件的默认大小
f.setSize(videoWidth + insetWidth, videoHeight + controlHeight + insetHeight);
f.validate();

//启动视频播放组件开始播放
player.start();
mediaPlayer.start();
} else if (ce instanceof EndOfMediaEvent) {
//当播放视频完成后,把时间进度条恢复到开始,并再次重新开始播放
player.setMediaTime(new Time(0));
player.start();
}
}
}JMF是专门用于做Java多媒体开发的。对于视频播放就是支持的格式少了一些,这个视频播放器可以播放mpg,avi,flv等等,想播放其他的请开发自己的插件。另外需要注明的是,有些不规范的avi格式的视频播放可能也会有问题。
下图是基于JMF和FFMPEG做的视频播放以及车牌识别定位的截图: 查看全部
Java JMF意为Java媒体框架(JMF)。该核心框架支持不同媒体(如:音频输出和视频输出)间的时钟同步。它是一个标准的扩展框架,允许用户制作纯音频流和视频流。
下面的代码展示的是利用JMF和Java Swing编写的一个简单的视频播放器,因此运行这段代码,首先请安装JMF,安装地址,自己百度Google,完整代码如下:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.media.CannotRealizeException;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.EndOfMediaEvent;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.NoPlayerException;
import javax.media.Player;
import javax.media.PrefetchCompleteEvent;
import javax.media.RealizeCompleteEvent;
import javax.media.Time;
@SuppressWarnings({ "restriction", "unused" })
public class JMFSample implements ControllerListener {
public static void main(String[] args) {
JMFSample sp = new JMFSample();
sp.play();
}

private Player mediaPlayer;
private Frame f;
private Player player;
private Panel panel;
private Component visual;
private Component control = null;

public void play(){
f = new Frame("JMF Sample1");
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
if(player != null) {
player.close();
}
System.exit(0);
}
});
f.setSize(500,400);
f.setVisible(true);
URL url = null;
try {
//准备一个要播放的视频文件的URL
url = new URL("file:/d:/2.mpg");
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
//通过调用Manager的createPlayer方法来创建一个Player的对象
//这个对象是媒体播放的核心控制对象
player = Manager.createPlayer(url);
} catch (NoPlayerException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
//对player对象注册监听器,能噶偶在相关事件发生的时候执行相关的动作
player.addControllerListener(this);

//让player对象进行相关的资源分配
player.realize();
}

private int videoWidth = 0;
private int videoHeight = 0;
private int controlHeight = 30;
private int insetWidth = 10;
private int insetHeight = 30;

//监听player的相关事件
public void controllerUpdate(ControllerEvent ce) {
if (ce instanceof RealizeCompleteEvent) {
//player实例化完成后进行player播放前预处理
player.prefetch();
} else if (ce instanceof PrefetchCompleteEvent) {
if (visual != null)
return;
//取得player中的播放视频的组件,并得到视频窗口的大小
//然后把视频窗口的组件添加到Frame窗口中,
if ((visual = player.getVisualComponent()) != null) {
Dimension size = visual.getPreferredSize();
videoWidth = size.width;
videoHeight = size.height;
f.add(visual);
} else {
videoWidth = 320;
}

//取得player中的视频播放控制条组件,并把该组件添加到Frame窗口中
if ((control = player.getControlPanelComponent()) != null) {
controlHeight = control.getPreferredSize().height;
f.add(control, BorderLayout.SOUTH);
}

//设定Frame窗口的大小,使得满足视频文件的默认大小
f.setSize(videoWidth + insetWidth, videoHeight + controlHeight + insetHeight);
f.validate();

//启动视频播放组件开始播放
player.start();
mediaPlayer.start();
} else if (ce instanceof EndOfMediaEvent) {
//当播放视频完成后,把时间进度条恢复到开始,并再次重新开始播放
player.setMediaTime(new Time(0));
player.start();
}
}
}
JMF是专门用于做Java多媒体开发的。对于视频播放就是支持的格式少了一些,这个视频播放器可以播放mpg,avi,flv等等,想播放其他的请开发自己的插件。另外需要注明的是,有些不规范的avi格式的视频播放可能也会有问题。
下图是基于JMF和FFMPEG做的视频播放以及车牌识别定位的截图:
13659471585430.jpg

使用in语句查询数据,并按照in里面的顺序排序

编程语言atool 发表了文章 • 0 个评论 • 343 次浏览 • 2016-04-22 11:19 • 来自相关话题

经常会用到用select * from tab where id in (1,2,3,4)语句。如果需要按照查询的id列表排列查询出来的数据记录的顺序该怎么做呢?
如下:
一、Accessselect * From 表 Where id in(1,5,3) order by instr(',1,5,3,',','&id&',')二、MSSQLselect * From 表 Where id in(1,5,3) order by charindex(','+rtrim(cast(id as varchar(10)))+',',',1,5,3,')三、MySql数据库
1.数字型in查询,db_node_id为intSELECT db_node_id, title FROM da_elements WHERE db_node_id IN (1342,1344,1343) ORDER BY FIELD( db_node_id, 1342,1344,1343 );2.字符型in查询,db_node_id为varcharSELECT db_node_id, title FROM da_elements WHERE db_node_id IN ('1342','1344','1343') ORDER BY FIELD( db_node_id, '1342','1344','1343' );Enjoy~ 查看全部
经常会用到用select * from tab where id in (1,2,3,4)语句。如果需要按照查询的id列表排列查询出来的数据记录的顺序该怎么做呢?
如下:
一、Access
select * From 表 Where id in(1,5,3) order by instr(',1,5,3,',','&id&',')
二、MSSQL
select * From 表 Where id in(1,5,3) order by charindex(','+rtrim(cast(id as varchar(10)))+',',',1,5,3,')
三、MySql数据库
1.数字型in查询,db_node_id为int
SELECT db_node_id, title FROM da_elements WHERE db_node_id IN (1342,1344,1343)  ORDER BY FIELD( db_node_id, 1342,1344,1343 );
2.字符型in查询,db_node_id为varchar
SELECT db_node_id, title FROM da_elements WHERE db_node_id IN ('1342','1344','1343')  ORDER BY FIELD( db_node_id, '1342','1344','1343' );
Enjoy~

SimSimi小黄鸡已经全面收费了,没有免费申请,只有7天trial key

其他类型atool 发表了文章 • 0 个评论 • 1103 次浏览 • 2016-04-22 11:15 • 来自相关话题

去年流行小黄鸡的时候(我也发了好几篇相关的文章的,具体可以本站搜索“小黄鸡”),我发了邮件申请小黄鸡接口,到现在才开始回复我邮件,邮件内容如下:

Hi.
We are excited to announce SimSimi developer site''s Grand Open!
SimSimi learnt more than 40 kind of language from various countries and culture area.
You can use these multilingual SimSimi AICR API database wherever you want.
You can just use the API for your project right away.
We also offer 7 day trial for free.
Visit http://developer.simsimi.com for more details.
Please feel free to reach out in case you have any other queries at api@simsimi.com
Best,
SimSimi Developer Team.

   我英文还可以,大致可以看懂,意思就是感谢你开发小黄鸡相关程序,然后介绍小黄鸡学习了40多中语言,多么牛逼云云,然后说他们提供7天的没费试用接口。于是我跑到官网看了下Contract菜单下面。内容大致如下:

SimSimi API has reasonable Plans and also easy to use.
We will offer you 7 days free trial as well. (1 trial key / account)
Sign up now!

意思是现在SimSimi小黄鸡接口依然很好用,并且提供每帐号7天的使用key,赶紧注册帐号吧~

这个页面我记得以前是说发送邮件到xxx邮箱申请接口。

看来人人网小黄鸡事件将SimSimi小黄鸡完全推向了收费的地步。中华人民共和国的人人大网为韩国棒子的SimSimi小黄鸡提高了不少的知名度。 查看全部
去年流行小黄鸡的时候(我也发了好几篇相关的文章的,具体可以本站搜索“小黄鸡”),我发了邮件申请小黄鸡接口,到现在才开始回复我邮件,邮件内容如下:

Hi.
We are excited to announce SimSimi developer site''s Grand Open!
SimSimi learnt more than 40 kind of language from various countries and culture area.
You can use these multilingual SimSimi AICR API database wherever you want.
You can just use the API for your project right away.
We also offer 7 day trial for free.
Visit http://developer.simsimi.com for more details.
Please feel free to reach out in case you have any other queries at api@simsimi.com
Best,
SimSimi Developer Team.

   我英文还可以,大致可以看懂,意思就是感谢你开发小黄鸡相关程序,然后介绍小黄鸡学习了40多中语言,多么牛逼云云,然后说他们提供7天的没费试用接口。于是我跑到官网看了下Contract菜单下面。内容大致如下:

SimSimi API has reasonable Plans and also easy to use.
We will offer you 7 days free trial as well. (1 trial key / account)
Sign up now!

意思是现在SimSimi小黄鸡接口依然很好用,并且提供每帐号7天的使用key,赶紧注册帐号吧~

这个页面我记得以前是说发送邮件到xxx邮箱申请接口。

看来人人网小黄鸡事件将SimSimi小黄鸡完全推向了收费的地步。中华人民共和国的人人大网为韩国棒子的SimSimi小黄鸡提高了不少的知名度。