JAVA调用直播接口详述
一、userId、appId、appSecret值的获取
在直播管理后台,点击开发设置,在显示的页面中获取userId、appId、appSecret值。
二、sign生成规则
将请求所需所有参数按照参数名字典顺序排列,连接参数名与参数值,并在首尾加上appSecret,然后计算MD5,将MD5结果转为16进制、大写字母,作为sign。
三、Java 接口调用示例
1、接口请求示例
private static void testBasic() {
String url = "http://api.polyv.net/live/v3/channel/basic/get";
// 用户对应的appId和加密串
String appId = "xxxxxxx";
String appSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
// 查询频道号
String channelId = "415174";
Map<String, String> params = new HashMap<>();
params.put("channelId", channelId);
// 调用Polyv的工具类方法设置sign
PolyvTool.setLiveSign(params, appId, appSecret);
// 调用Polyv的httpClient工具类发送请求
String content = HttpClientUtil.getInstance()
.sendHttpGet(url + "?" + PolyvTool.mapJoinNotEncode(params));
System.out.println(content);
}
2、生成sign结果示例
// 请求参数(这里为频道号)
channelId = "415174";
// appId和appSecret
appId = "egyszbtsd58";
appSecret = "6ef8d34c08f44e91a18e28d42ff0da7e";
// 时间戳
timeStamp = "1594369061376";
// 拼接后字符串
6ef8d34c08f44e91a18e28d42ff0da7eappIdegyszbtsd58channelId415174timestamp15943690613766ef8d34c08f44e91a18e28d42ff0da7e
// 加密串
7DEDD50579ECB5E5062954DBA10ADCA1
四、Java sign签名生成工具类
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* polyv工具类
*/
public class PolyvTool {
/**
* 获取加密串
*/
public static void setLiveSign(Map<String, String> params, String appId, String key) {
params.put("sign", getSign(params, appId, key));
}
/**
* 获取加密串
*/
public static String getSign(Map<String, String> params, String appId, String key) {
params.put("appId", appId);
params.put("timestamp", String.valueOf(System.currentTimeMillis()));
// 处理参数,计算MD5哈希值
String concatStr = concatParams(params);
String plain = key + concatStr + key;
System.out.println("request signStr=" + plain);
String encrypted = md5Hex(plain);
// 32位大写MD5值
return encrypted.toUpperCase();
}
/**
* 把数组所有元素排序,并按照“参数参数值”的模式成字符串,用于计算MD5哈希值
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String concatParams(Map<String, String> params) {
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder sb = new StringBuilder();
for (String key : keys) {
String value = params.get(key);
if (isBlank(value)) {
continue;
}
sb.append(key).append(value);
}
return sb.toString();
}
/**
* 对字符串做MD5加密,返回加密后的字符串。
* @param text 待加密的字符串。
* @return 加密后的字符串。
*/
public static String md5Hex(String text) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] inputByteArray = text.getBytes();
messageDigest.update(inputByteArray);
byte[] resultByteArray = messageDigest.digest();
return byteArrayToHex(resultByteArray).toLowerCase();
} catch (NoSuchAlgorithmException e) {
return null;
}
}
/**
* 将字节数组换成成16进制的字符串
* @param byteArray 字节
* @return 字符串
*/
public static String byteArrayToHex(byte[] byteArray) {
// 初始化一个字符数组用来存放每个16进制字符
char[] hexDigits = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };
// new一个字符数组,这个就是用来组成结果字符串的(一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方))
char[] resultCharArray =new char[byteArray.length * 2];
// 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
int index = 0;
for (byte b : byteArray) {
resultCharArray[index++] = hexDigits[b>>> 4 & 0xf];
resultCharArray[index++] = hexDigits[b& 0xf];
}
// 字符数组组合成字符串返回
return new String(resultCharArray);
}
/**
* url 参数串连但是不进行参数Encode
* @param map map
* @return string
*/
public static String mapJoinNotEncode(Map<String, String> map) {
StringBuilder stringBuilder = new StringBuilder();
int i = 0;
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (isBlank(value)) {
continue;
}
if (0 != i) {
stringBuilder.append("&");
}
stringBuilder.append(key).append("=").append(map.get(key));
i++;
}
return stringBuilder.toString();
}
/**
* url 参数串连
* @param map map
* @param keyLower keyLower
* @param valueUrlEncode valueUrlEncode
* @return string
*/
public static String mapJoin(Map<String, String> map, boolean keyLower, boolean valueUrlEncode) {
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (isBlank(value)){
continue;
}
try {
String temp = (key.endsWith("_") && key.length() > 1) ? key.substring(0, key.length() - 1) : key;
stringBuilder.append(keyLower ? temp.toLowerCase() : temp)
.append("=")
.append(valueUrlEncode ? URLEncoder.encode(value, "utf-8").replace("+", "%20") : value)
.append("&");
} catch (UnsupportedEncodingException e) {
System.out.println(e.getMessage());
}
}
if (stringBuilder.length() > 0) {
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
}
return stringBuilder.toString();
}
/**
* 判断字符是否为空
* @param cs 字符
* @return true/false
*/
public static boolean isBlank(CharSequence cs) {
int strLen;
if (cs != null && (strLen = cs.length()) != 0) {
for(int i = 0; i < strLen; ++i) {
if (!Character.isWhitespace(cs.charAt(i))) {
return false;
}
}
return true;
} else {
return true;
}
}
}
五、Java http请求调用工具类
注:该工具类仅为示例(非必须),客户可以按照自己的方式进行接口请求,如需在生产环境使用该工具类,客户需按需进行httpClient的一些配置优化
1、使用该工具类需要引入apache的工具包,下面提供该工具包的maven引用方式
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5</version>
</dependency>
2、http请求工具类代码
import java.io.File;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
/**
* httpclient的操作实现类
*/
public class HttpClientUtil {
private static final String QUESTION_MARK = "?";
private static final String DEFAULT_ENCODING = "UTF-8";
private static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";
public static final String APPLICATION_FORM_URLENCODED_VALUE = "application/x-www-form-urlencoded";
private final RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000)
.setConnectionRequestTimeout(10000).build();
private static HttpClientUtil instance = null;
private HttpClient ignoreSslClient;
private HttpClient client;
private HttpClientUtil() {
this.ignoreSslClient = createHttpsClient(true);
this.client = createHttpsClient(false);
}
public static HttpClientUtil getInstance() {
if (instance == null) {
instance = new HttpClientUtil();
}
return instance;
}
private HttpClient getHttpClient(boolean ignoreSsl) {
return ignoreSsl ? ignoreSslClient : client;
}
/**
* 发送 post请求
* @param httpUrl 地址
* @return 请求结果
*/
public String sendHttpPost(String httpUrl) {
HttpPost httpPost = new HttpPost(httpUrl);
return sendHttpPost(httpPost);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param params 参数(格式:json格式的内容)
* @param headers 请求头
* @return 请求结果
*/
public String sendHttpPostJson(String httpUrl, String params, Map<String, String> headers) {
return sendHttpPostJson(httpUrl, params, headers, false);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param params 参数(格式:json格式的内容)
* @param headers 请求头
* @param ignoreSsl 是否跳过证书校验,true为跳过 false为不跳过
* @return 请求结果
*/
public String sendHttpPostJson(String httpUrl, String params, Map<String, String> headers, boolean ignoreSsl) {
// 创建httpPost
HttpPost httpPost = new HttpPost(httpUrl);
try {
// 设置参数
StringEntity stringEntity = new StringEntity(params, DEFAULT_ENCODING);
stringEntity.setContentType(APPLICATION_JSON_UTF8_VALUE);
httpPost.setEntity(stringEntity);
if (null != headers && headers.size() > 0) {
for (String name : headers.keySet()) {
httpPost.addHeader(name, headers.get(name));
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sendHttpRequest(httpPost, ignoreSsl);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param maps 参数
* @param body 请求体
* @return 请求结果
*/
public String sendHttpPost(String httpUrl, Map<String, String> maps, String body) {
String paramStr = PolyvTool.mapJoinNotEncode(maps);
if (null != paramStr) {
paramStr = QUESTION_MARK + paramStr;
}
String urlStr = httpUrl + paramStr;
HttpPost httpPost = new HttpPost(urlStr);
try {
StringEntity entity = new StringEntity(body, DEFAULT_ENCODING);
entity.setContentType(APPLICATION_JSON_UTF8_VALUE);
httpPost.setEntity(entity);
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sendHttpRequest(httpPost, true);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param params 参数(格式:key1=value1&key2=value2)
* @return 请求结果
*/
public String sendHttpPost(String httpUrl, String params) {
return sendHttpPostWithHeader(httpUrl, params, null);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param headers 头
* @param params 参数(格式:key1=value1&key2=value2)
* @return 请求结果
*/
public String sendHttpPostWithHeader(String httpUrl, String params, Header[] headers) {
HttpPost httpPost = new HttpPost(httpUrl);
try {
if (null != headers) {
httpPost.setHeaders(headers);
}
// 设置参数
StringEntity stringEntity = new StringEntity(params, DEFAULT_ENCODING);
stringEntity.setContentType(APPLICATION_FORM_URLENCODED_VALUE);
httpPost.setEntity(stringEntity);
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sendHttpPost(httpPost);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param maps 参数
* @return 请求结果
*/
public String sendHttpPost(String httpUrl, Map<String, String> maps) {
return sendHttpPostWithHeader(httpUrl, maps, null);
}
/**
* 发送 post请求
* @param httpUrl 地址
* @param maps 参数
* @return 请求结果
*/
public String sendHttpPostWithHeader(String httpUrl, Map<String, String> maps, Header[] headers) {
HttpPost httpPost = new HttpPost(httpUrl);
if (null != headers) {
httpPost.setHeaders(headers);
}
// 创建参数队列
List<NameValuePair> nameValuePairs = new ArrayList<>();
for (String key : maps.keySet()) {
nameValuePairs.add(new BasicNameValuePair(key, maps.get(key)));
}
try {
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, DEFAULT_ENCODING));
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sendHttpPost(httpPost);
}
/**
* 发送 get请求
* @param httpUrl 地址
* @param headers 请求头
* @return 请求结果
*/
public String sendHttpGet(String httpUrl, Map<String, String> headers) {
// 创建httpGet
HttpGet httpGet = new HttpGet(httpUrl);
try {
if (null != headers && headers.size() > 0) {
for (String name : headers.keySet()) {
httpGet.addHeader(name, headers.get(name));
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sendHttpGet(httpGet);
}
/**
* 发送 post请求(带文件)
* @param httpUrl 地址
* @param maps 参数
* @param fileLists 附件
* @return 请求结果
*/
public String sendHttpPost(String httpUrl, Map<String, String> maps, List<File> files, String fileParamName) {
HttpPost httpPost = new HttpPost(httpUrl);
MultipartEntityBuilder meBuilder = MultipartEntityBuilder.create();
// 设置此参数后可以保证服务器拿到的文件名不会出现乱码
meBuilder.setMode(HttpMultipartMode.RFC6532);
for (String key : maps.keySet()) {
meBuilder.addPart(key, new StringBody(maps.get(key), ContentType.TEXT_PLAIN));
}
for (File file : files) {
FileBody fileBody = new FileBody(file);
fileParamName = (null == fileParamName) ? "files" : fileParamName;
meBuilder.addPart(fileParamName, fileBody);
}
HttpEntity reqEntity = meBuilder.build();
httpPost.setEntity(reqEntity);
return sendHttpPost(httpPost);
}
/**
* 发送 post请求(带文件)
* @param httpUrl 地址
* @param maps 参数
* @param file 上传文件
* @param fileParamName 上传文件参数名
*/
private static String sendHttpPost(String httpUrl, Map<String, String> maps, File file, String fileParamName) {
fileParamName = (null == fileParamName) ? "file" : fileParamName;
return sendHttpPost(httpUrl, maps, file, fileParamName);
}
/**
* 发送Post请求
* @param httpPost post请求
* @return 请求结果
*/
private String sendHttpPost(HttpPost httpPost) {
return sendHttpRequest(httpPost);
}
/**
* 发送 get请求
* @param httpUrl 请求的路径
* @return 成功时为响应内容,失败时为 null
*/
public String sendHttpGet(String httpUrl) {
// 创建get请求
HttpGet httpGet = new HttpGet(httpUrl);
return sendHttpGet(httpGet);
}
/**
* 发送DELETE请求
* @param httpUrl 请求的路径
* @return 成功时为响应内容,失败时为 null
*/
public String sendHttpDelete(String httpUrl) {
HttpDelete httpDelete = new HttpDelete(httpUrl);
return sendHttpRequest(httpDelete);
}
/**
* 发送 get请求Https
* @param httpUrl 请求的路径
* @param ignoreSSLCerts 是否忽略证书校验
*/
public String sendHttpsGet(String httpUrl, boolean ignoreSslCerts) {
HttpGet httpGet = new HttpGet(httpUrl);
return sendHttpsGet(httpGet, ignoreSslCerts);
}
/**
* 发送Get请求
* @param httpGet 请求
* @return 成功时为响应内容,失败时为 null
*/
private String sendHttpGet(HttpGet httpGet) {
return sendHttpRequest(httpGet);
}
/**
* 发送Get请求
* @param requestBase 请求
* @return 成功时为响应内容,失败时为 null
*/
private String sendHttpRequest(HttpRequestBase requestBase) {
return sendHttpRequest(requestBase, false);
}
/**
* 发送请求
* @param requestBase 请求
* @param ignoreSslCerts 忽略证书和域名校验
* @return 成功时为响应内容,失败时为 null
*/
private String sendHttpRequest(HttpRequestBase requestBase, boolean ignoreSslCerts) {
HttpResponse response = null;
HttpEntity entity;
try {
// 创建默认的httpClient实例.
requestBase.setConfig(requestConfig);
// 执行请求
response = getHttpClient(ignoreSslCerts).execute(requestBase);
entity = response.getEntity();
return EntityUtils.toString(entity, DEFAULT_ENCODING);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
HttpClientUtils.closeQuietly(response);
}
return null;
}
/**
* 发送Get请求Https
* ignoreSSLCerts参数为true可以忽略证书和域名的校验,可以避免 {@link sun.security.validator.ValidatorException}
* @param httpGet 使用https发送get请求
* @param ignoreSSLCerts 忽略证书和域名校验
* @return 返回内容
*/
private String sendHttpsGet(HttpGet httpGet, boolean ignoreSslCerts) {
HttpResponse response = null;
String responseContent = null;
try {
httpGet.setConfig(requestConfig);
// 执行请求
response = getHttpClient(ignoreSslCerts).execute(httpGet);
HttpEntity entity = response.getEntity();
responseContent = EntityUtils.toString(entity, DEFAULT_ENCODING);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
HttpClientUtils.closeQuietly(response);
}
return responseContent;
}
/**
* 初始化https请求Client
* @param ignoreSSLCerts 忽略证书
*/
private CloseableHttpClient createHttpsClient(boolean ignoreSslCerts) {
CloseableHttpClient httpClient;
if (ignoreSslCerts) {
SSLContext ctx = null;
try {
// 忽略客户端证书校验
ctx = new SSLContextBuilder().loadTrustMaterial(null, (chain, authType) -> true).build();
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
System.out.println("create ssl context error");
}
// 忽略证书和hostName校验
httpClient = HttpClients.custom()
.setSSLHostnameVerifier((host, session) -> true)
.setSSLSocketFactory(new SSLConnectionSocketFactory(ctx)).build();
} else {
httpClient = HttpClients.createDefault();
}
return httpClient;
}
签名生成规则
一、userId、appId、appSecret值的获取
在直播管理后台,点击开发设置,在显示的页面中获取userId、appId、appSecret值。
二、sign生成规则
将请求所需所有参数按照参数名字典顺序排列,连接参数名与参数值,并在首尾加上appSecret,然后计算MD5,将MD5结果转为16进制、大写字母,作为sign。
三、Java sign生成示例 (详细请查看:JAVA调用直播接口详述)
String appId = "XXXXXXXX";
String userId = "XXXXXXXX";
String appSecret = "XXXXXXXXXXXXXXXXXXXXXXXX";
long ts = System.currentTimeMillis();
// 创建参数表 (创建接口需要传递的所有参数表)
Map<String, String> paramMap = new HashMap<String, String>();
paramMap.put("appId", appId);
paramMap.put("timestamp", Long.toString(ts));
//对参数名进行字典排序
String[] keyArray = paramMap.keySet().toArray(new String[0]);
Arrays.sort(keyArray);
//拼接有序的参数串
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(appSecret);
for (String key : keyArray) {
stringBuilder.append(key).append(paramMap.get(key));
}
stringBuilder.append(appSecret);
String signSource = stringBuilder.toString();
String sign = org.apache.commons.codec.digest.DigestUtils.md5Hex(signSource).toUpperCase();
System.out.println("http://api.polyv.net/live/v1/users/" + userId + "/channels?appId=" + appId + "×tamp=" + ts + "&sign=" + sign);
四、PHP sign生成示例
1、config.php文件代码
<?php
//签名验证必需参数
$appId = "XXXXXXXXX";
$timestamp = time()*1000;
$appSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
//获取sign函数
function getSign($params){
global $appSecret;
// 1. 对加密数组进行字典排序
foreach ($params as $key=>$value){
$arr[$key] = $key;
}
sort($arr);
$str = $appSecret;
foreach ($arr as $k => $v) {
$str = $str.$arr[$k].$params[$v];
}
$restr = $str.$appSecret;
$sign = strtoupper(md5($restr));
return $sign;
}
?>
2、接口请求示例
<?php
//引用config.php
include 'config.php';
//接口需要的参数(非sign)赋值
$userId = "XXXXXXXXX";
$params = array(
'appId'=>$appId,
'timestamp'=>$timestamp
);
//生成sign
$sign = getSign($params); //详细查看config.php文件的getSign方法
//接口请求url
$url ="http://api.polyv.net/live/v1/users/".$userId."/channels?appId=".$appId."×tamp=".$timestamp."&sign=".$sign;
//输出接口请求结果
$ch = curl_init() or die ( curl_error() );
curl_setopt( $ch, CURLOPT_URL, $url);
curl_setopt ($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, 360);
$response = curl_exec ( $ch );
curl_close ( $ch );
//打印获得的数据
print_r($response);
?>