qiniu / java-sdk Goto Github PK
View Code? Open in Web Editor NEWQiniu Resource (Cloud) Storage SDK for Java
License: MIT License
Qiniu Resource (Cloud) Storage SDK for Java
License: MIT License
在某些情况下,希望可以中断上传,并取消。
有没有考虑过去除对okhttp 的依赖,gson 的依赖。
java 中有httpclient,okhttp,URLConnect 等很多第三方可以请求和解析的http 类的包,json 的世界也是很多:jackson1,2,fastjson,gson 等很多~
不知道是否可以选择性的使用http 组件和json 组件呢
最新的fetch接口返回值更加丰富,参考文档:https://developer.qiniu.com/kodo/api/fetch
定义新的接口
public class FetchRet {
public String key;
public String hash;
public String mimeType;
public long fsize;
}
提个建议哈,能不能不要eclipse相关的文件上传上来,毕竟有些童鞋不是用eclipse的
测试 API
如题。
debug 结果显示
第 39 行存在问题,NullPointerException
httpClient.networkInterceptors().add(new Interceptor() {
@Override
public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
com.squareup.okhttp.Response response = chain.proceed(request);
IpTag tag = (IpTag) request.tag();
第39行 -> String ip = chain.connection().getSocket().getRemoteSocketAddress().toString();
tag.ip = ip;
return response;
}
});
chain.connection 低概率为 null
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.IncompatibleClassChangeError: Found class com.squareup.okhttp.Connection, but interface was expected
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:972)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.springframework.web.filter.ShallowEtagHeaderFilter.doFilterInternal(ShallowEtagHeaderFilter.java:58)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IncompatibleClassChangeError: Found class com.squareup.okhttp.Connection, but interface was expected
at com.qiniu.http.Client$1.intercept(Client.java:39)
at com.squareup.okhttp.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:802)
at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:683)
at com.squareup.okhttp.Call.getResponse(Call.java:272)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:228)
Jul 04, 2016 2:55:32 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [spring] in context with path [] threw exception [Handler processing failed; nested exception is java.lang.IncompatibleClassChangeError: Found class com.squareup.okhttp.Connection, but interface was expected] with root cause
java.lang.IncompatibleClassChangeError: Found class com.squareup.okhttp.Connection, but interface was expected
at com.qiniu.http.Client$1.intercept(Client.java:39)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:199)
at com.squareup.okhttp.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:802)
at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:683)
at com.squareup.okhttp.Call.getResponse(Call.java:272)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:228)
at com.squareup.okhttp.Call.execute(Call.java:79)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:199)
at com.qiniu.http.Client.send(Client.java:195)
at com.squareup.okhttp.Call.execute(Call.java:79)
at com.qiniu.http.Client.post(Client.java:132)
at com.qiniu.http.Client.send(Client.java:195)
at com.qiniu.http.Client.post(Client.java:115)
at com.qiniu.http.Client.post(Client.java:132)
at com.qiniu.storage.BucketManager.post(BucketManager.java:319)
at com.qiniu.http.Client.post(Client.java:115)
at com.qiniu.storage.BucketManager.ioPost(BucketManager.java:309)
at com.qiniu.storage.BucketManager.post(BucketManager.java:319)
at com.qiniu.storage.BucketManager.fetch(BucketManager.java:263)
at com.qiniu.storage.BucketManager.ioPost(BucketManager.java:309)
at com.talicai.server.common.util.QiNiuUploadPicture.uploadPictureUrl(QiNiuUploadPicture.java:65)
at com.qiniu.storage.BucketManager.fetch(BucketManager.java:263)
at com.sun.proxy.$Proxy35.createNewSNSUser(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at com.talicai.server.controller.bill.V3.UserController.loginSNS(UserController.java:212)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:436)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:436)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
... 32 more
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.springframework.web.filter.ShallowEtagHeaderFilter.doFilterInternal(ShallowEtagHeaderFilter.java:58)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
java.lang.IncompatibleClassChangeError: Found class com.squareup.okhttp.Connection, but interface was expected
jdk环境1.7+
jar包依赖:
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.0.0, 7.0.99]</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>1.6.0</version>
</dependency>
请问到底什么版本的okhttp和okio是可以正常使用的?
putpolicy中包含callbackBody时,putpolicy序列化json后,&会被转义成\u0026
会不会是Gson序列化的问题
响应:invalid multipart format: multipart: message too large
reqId:wgcAALSIcXFrHH0U
代码:
http://7xv25h.com1.z0.glb.clouddn.com/error.PNG
String s = Json.encode(x);
s的值为:
{"scope":"tontisa-test-xqerp","fsizeLimit":15728640,"deadline":1476371165} 设置为15m还是有问题
sdk 版本 7.1.3
错误日志:
com.qiniu.common.QiniuException
at com.qiniu.http.Client.send(Client.java:245)
at com.qiniu.http.Client.multipartPost(Client.java:218)
at com.qiniu.http.Client.multipartPost(Client.java:186)
at com.qiniu.storage.FormUploader.upload(FormUploader.java:52)
at com.qiniu.storage.UploadManager.put(UploadManager.java:126)
at com.qiniu.storage.UploadManager.put(UploadManager.java:104)
at io.cloudinsight.alert.service.impl.QiniuImageStoreService.upload(QiniuImageStoreService.java:40)
报错挺频繁的
public String uploadTokenWithDeadline(String bucket, String key, long deadline, StringMap policy, boolean strict) {
// TODO UpHosts Global
String scope = bucket;
if (key != null) {
scope = bucket + ":" + key;
}
StringMap x = new StringMap();
copyPolicy(x, policy, strict);
x.put("scope", scope);
x.put("deadline", deadline);
String s = Json.encode(x);
return signWithData(StringUtils.utf8Bytes(s));
}
public String uploadTokenWithPolicy(Object obj) {
String s = Json.encode(obj);
return signWithData(StringUtils.utf8Bytes(s));
}
可以参考 v3 api : http://docs.qiniutek.com/v3/api/io/#upload-file
并需补充相应的单元测试
SDK git 主库里边的 docs/README.md 和 SDK 暴露的API方法严重脱节,需要重新整理。可以先拟个文档的目录结构。
范例参考:
http://docs.qiniutek.com/v3/sdk/ruby/
https://github.com/qiniu/php5-sdk/pull/15/files
java.lang.ArithmeticException: / by zero12994787 : 0 -- 0 : 0%
at com.examples.client.ResumeableDemo$1.run(ResumeableDemo.java:145)
分块上传Demo中报除0错误
Zone.java
public String rsHost(String ak, String bucket) { throw new UnsupportedOperationException(); }
现在的artifactId是sdk
,最终打包进war里面的文件名就是sdk.jar
,会跟其他的无数的 jar 文件一起放在 WEB-INF/lib
下面,这样很没有标识度的文件名,是非常容易跟其他的同样没有标识度的 jar 文件冲突的。
建议将 artifactId 修改成 qiniu-sdk
,这样最终的jar文件将会是qiniu-sdk.jar
,会显得更专业
该接口主要用来设置文件的存活时间,单位天数
之所以暴露这个类,主要是方便用户对Zone进行自定义,用于某些特定的场合。
文档中讲“分片上传过程中每个块内部只能按顺序逐一上传该块所切分好的片。而每个块之间相互独立,因此若干个块可以同时进行传输而不会相互干扰,因此我们可以利用这个特征实现并发上传特性。”
可否给一个例子 谢谢
http://developer.qiniu.com/code/v7/sdk/java.html JavaSDK文档中 SDK 的下载链接存在错误
http://search.maven.org/##search%7Cgav%7C1%7Cg%3A%22com.qiniu%22%20AND%20a%3A%22qiniu-java-sdk%22 此链接中search前多了一个#符号,导致无法显示正常结果
代码在encodeBase64Ex中实际上最终调用了jdk提供的Base64编码,那么URL版本的Base64编码与传统Base64编码区别在哪?仅仅是62,63位的字符由+变成-,由/变成_,详情参看http://www.ietf.org/rfc/rfc4648.txt。
public static byte[] urlsafeEncodeBytes(byte[] src) {
if (src.length % 3 == 0) {
return encodeBase64Ex(src);
}
byte[] b = encodeBase64Ex(src);
if (b.length % 4 == 0) {
return b;
}
int pad = 4 - b.length % 4;
byte[] b2 = new byte[b.length + pad];
System.arraycopy(b, 0, b2, 0, b.length);
b2[b.length] = '=';
if (pad > 1) {
b2[b.length + 1] = '=';
}
return b2;
}
这段代码中的
int pad = 4 - b.length % 4;
byte[] b2 = new byte[b.length + pad];
System.arraycopy(b, 0, b2, 0, b.length);
b2[b.length] = '=';
if (pad > 1) {
b2[b.length + 1] = '=';
}
这段逻辑什么时候能用上,实在无法理解,传统Base64算法里,会自动对二进制位数不足进行补齐,满足6位编码的需要,所以这段代码应该是多余的,除非传统的Base64源码算法不正确。
亲们,希望增加能用于七牛云存储的 Apache Wagon 的 Maven 模块。
使用该模块,用户应可以将七牛云存储当着自己的私有 maven 仓库,可将 artifacts 提交到该仓库,并从该仓库获取指定的 artifacts。
用户应可以将项目的 maven site 发布到七牛的公开空间,方便更多人看到。
亦即,该模块应该实现 Apache Wagon API 的绝大部分功能,可以操作文件和文件夹,可以操作(上传和下载)七牛云存储上自己账户下的公开和私有空间。
运行时发现这个package 并不存在 请问这个对应的代码 或jar在哪里
qiniu.happydns.xxx
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.2.0, 7.2.99]</version>
</dependency>
jdk1.7一直报这个错,又看不到七牛SDK构建版本是多少,在官网找了好久也没有在找到相关的技术支持的(这个不应该吧,连在线的技术支持都没有。。。)
Request.Builder no longer accepts null if a request body is required. Passing null will now fail for request methods that require a body. Instead use an empty body such as this one:
RequestBody.create(null, new byte[0]);
会抛出异常,需要改下传入参数
使用 bucketManager.listFiles
无法正确获取 commonPrefixes
FileListing 未对 commonPrefixes 进行解析
目前七牛有华东,华北,华南,北美几个机房,其中 rs, rsf, api 服务都可以共用域名,即使是北美访问也是经过加速的。
rs.qiniu.com
rsf.qiniu.com
api.qiniu.com
另外这组域名同时配置了http和https的协议。
看到不少代码里面捕获异常后都是直接输出到控制台的,建议使用 logger 来处理。
最新版报错
`
Exception in thread "main" java.lang.NoClassDefFoundError: com/squareup/okhttp/RequestBody
at com.qiniu.storage.BucketManager.(BucketManager.java:29)
at com.test.QiniuDown.main(QiniuDown.java:19)
Caused by: java.lang.ClassNotFoundException: com.squareup.okhttp.RequestBody
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 2 more
`
不少用 java sdk 的用户希望能控制下载URL的有效期
可否给出上传进度的回调?
另外,也可以在回调中提供终止上传的办法。
{"success":true,"name":"sunflowerb.jpg"}
,然而github上的关于回调流程.md
说明示例给的是{"response": "success"}
。由于这个返回消息会透传给客户端,那么这个格式是否有要求?当使用相同的KEY,上次文件的时候
如果 我设置了scope为bucket:key 为这种模式的时候 7牛服务端 报 bad token
如果 我设置 scope为bucket为这种模式的时候 7牛服务端 报 File already exist
这个问题困扰很久了,希望能及时回复
对于stat命令的batch,会返回一系列的数据或者error,对于move,delete,copy,chgm的batch会返回操作结果或error信息,所以使用结构体如下:
public final class BatchStatus {
/**
* 批量请求的每个命令的执行结果状态码
*/
public int code;
/**
* 批量请求的每个命令返回的结果
*/
public BatchOpData data;
}
/**
* 该类封装了batch接口回复中的data部分
* 参考文档:<a href="https://developer.qiniu.com/kodo/api/batch">批量操作</a>
*/
public class BatchOpData {
//batch stat结果
public long fsize;
public String hash;
public String mimeType;
public long putTime;
//batch move, copy, delete, chgm结果
public String error;
}
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.0.0, 7.1.99]</version>
</dependency>
内部okhttp依赖
okhttp 3.3.1
okio 1.8.0
看了下ResumeUploader的源码,对于参与 定义 有些不是很明白,能给ResumeUploader的Demo么?
http://developer.qiniu.com/docs/v6/sdk/java-sdk.html#rsf-listPrefix
文档上写到的createFileListIterator
方法获取资料列表, 为什么我一直获取的只有一个呢?
查看了代码,发现 Client 的连接池大小是写死的,并发不够大, 是否可配置呢?
java-sdk/src/main/java/com/qiniu/http/Client.java
Lines 45 to 47 in 123ea80
dispatcher.setMaxRequests(64);
dispatcher.setMaxRequestsPerHost(16);
ConnectionPool connectionPool = new ConnectionPool(32, 5, TimeUnit.MINUTES);
其他的库都升级到okhttpd 3.0了,为了qiniu还保留了okhttp 2.7.2
所有版本的sdk都支持jdk6吗,或者支持jdk6的最新版本是哪个?
我司长期使用 7.0.3 版本。在一些 TestCase 里面,需要 mock 一些类,比如 OperationManager
。
然而新版本 7.1.2
将这类 Manager 设置为 final
,作为一个 Library,将 Service 类型设置为 final,虽然保证了外部无法进行修改,但是也同时关上一扇门。后果是之一是导致无法 mock 进行单元测试,甚为遗憾。
能否考虑一下用户的这类场景,将 final class 去掉?
假如我从一个流过来,必须得转换成byte[]
所以建议支持inputStream输入
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.