spring boot 集成腾讯文本翻译遇到的那些坑
第一步:根据api文档,先生成api秘钥
https://cloud.tencent.com/document/product/551/16611
第二步:编写http请求工具类,网上有很多
第三步:编写返回结果的实体类
package com.central.util.tencent;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@Data
public class TencentResult {
/**
* 翻译后的文本
*/
private String TargetText;
private String Source;//源语言
private String Target;//目标语言
private String RequestId;//唯一请求ID,每次请求都会返回
}
第五步:编写翻译工具类,借鉴了某位大神的代码,自己加以修改了一下
package com.central.util.tencent;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.central.util.baidu.HttpUtil;
import com.central.util.baidu.TransResult;
import com.google.common.collect.Maps;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.RandomUtils;
import springfox.documentation.spring.web.json.Json;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class TencentTrans {
private static final String TRANS_API_HOST = "https://tmt.tencentcloudapi.com/";
private static final Charset UTF_8 = StandardCharsets.UTF_8;
private static final String SECRET_ID = "#######";//填写自己的SECRET_ID
private static final String SECRET_KEY = "############";//填写自己SECRET_KEY
public static String getTransResult(String content,String from ,String to) throws IOException {
Map<String,String> params = buildParams(content,from,to);
JSONObject jsonObject = null;
//译文长度大于2000,用POST请求
if (content.length() >= 200) {
jsonObject = HttpUtil.doPost(TRANS_API_HOST,params);
} else {
String url = getUrlWithQueryString(TRANS_API_HOST,params);
System.out.println("请求地址:" + url);
jsonObject = HttpUtil.doGet(url);
}
Map map = JSON.parseObject(jsonObject.get("Response").toString());
String requestId = map.get("RequestId").toString();
if (map.get("Error") != null) {
JSONObject obj = (JSONObject)map.get("Error");
String code = obj.getString("Code");
String message = obj.getString("Message");
return "翻译失败,原因:" + message;
} else {
TencentResult result = JSONObject.parseObject(jsonObject.get("Response").toString(),TencentResult.class);
System.out.println("翻译结果:"+result.getTargetText());
return "翻译结果:"+result.getTargetText();
}
}
public static Map<String,String> buildParams (String content,String from,String to) {
Map<String,String> paramMap = Maps.newLinkedHashMapWithExpectedSize(10);
paramMap.put("Action", "TextTranslate");
paramMap.put("Nonce", String.valueOf(RandomUtils.nextInt(10,1000)));
paramMap.put("ProjectId", "0");// 请填写自己的ProjectId
paramMap.put("Region", "ap-shanghai");
paramMap.put("SecretId", SECRET_ID);
paramMap.put("Source",from);
paramMap.put("SourceText", content);
paramMap.put("Target", to);
// 此处的时间戳要格外注意,是Unix时间戳
paramMap.put("Timestamp", String.valueOf(System.currentTimeMillis() / 1000));
paramMap.put("Version","2018-03-21");
// 构造签名信息
StringBuilder builder = new StringBuilder();
for (String key : paramMap.keySet()) {
// 若输入参数中包含下划线,则需要将其转换为“.”
builder.append('&').append(key).append('=').append(paramMap.get(key).replace('_', '.'));
}
builder = builder.deleteCharAt(0);
System.out.println("请求字符串:" + builder.toString());
String str = "GETtmt.tencentcloudapi.com/?" + builder.toString();
str = Base64.encodeBase64String(hmacSha1(str, SECRET_KEY));
System.out.println("签名串:" + str);
str = encode(str);
System.out.println("签名串编码后:" + str);
paramMap.put("Signature",str);
return paramMap;
}
public static byte[] hmacSha1(String src, String key){
try {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes("utf-8"),"HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);
return mac.doFinal(src.getBytes("utf-8"));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String getUrlWithQueryString(String url,Map<String,String> params){
if (params == null) {
return url;
}
StringBuilder builder = new StringBuilder(url);
if (url.contains("?")){
builder.append("&");
} else {
builder.append("?");
}
int i = 0;
for (String key : params.keySet()) {
String value = params.get(key);
if(value == null){//过滤空的key
continue;
}
if (i != 0) {
builder.append("&");
}
builder.append(key);
builder.append("=");
builder.append(encode(value));
i++;
}
return builder.toString();
}
public static String encode(String input){
if(input == null){
return "";
}
try {
return URLEncoder.encode(input,"utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return input;
}
public static void main(String[] args) throws IOException {
String result = getTransResult("皇帝岛珊瑚岛是普吉必玩岛屿,精致绝美的天然热带岛屿风光,珊瑚礁的海岸景色","auto","en");
System.out.println(result);
}
}
到了这里,开始进行测试啦!满心期待,一定可以成功,然而结果却不如人意,出现了异常,签名错误
翻译失败,原因:The provided credentials could not be validated. Please check your signature is correct.
什么情况,步骤没错啊,怎么会报异常呢?检查代码,嗯,没有发现什么异常,怎么会执行不下去呢,百思不得其解,再次查看api文档,可能是签名转码的问题,抱着试一试的态度,将buildParams方法中URLEncode这一步注释掉,执行正常,返回翻译结果
public static Map<String,String> buildParams (String content,String from,String to) {
Map<String,String> paramMap = Maps.newLinkedHashMapWithExpectedSize(10);
paramMap.put(“Action”, “TextTranslate”);
paramMap.put(“Nonce”, String.valueOf(RandomUtils.nextInt(10,1000)));
paramMap.put(“ProjectId”, “0”);// 请填写自己的ProjectId
paramMap.put(“Region”, “ap-shanghai”);
paramMap.put(“SecretId”, SECRET_ID);
paramMap.put(“Source”,from);
paramMap.put(“SourceText”, content);
paramMap.put(“Target”, to);
// 此处的时间戳要格外注意,是Unix时间戳
paramMap.put(“Timestamp”, String.valueOf(System.currentTimeMillis() / 1000));
paramMap.put(“Version”,“2018-03-21”);
// 构造签名信息
StringBuilder builder = new StringBuilder();
for (String key : paramMap.keySet()) {
// 若输入参数中包含下划线,则需要将其转换为“.”
builder.append('&').append(key).append('=').append(paramMap.get(key).replace('_', '.'));
}
builder = builder.deleteCharAt(0);
System.out.println("请求字符串:" + builder.toString());
String str = "GETtmt.tencentcloudapi.com/?" + builder.toString();
str = Base64.encodeBase64String(hmacSha1(str, SECRET_KEY));
System.out.println("签名串:" + str);
//str = encode(str);
//System.out.println("签名串编码后:" + str);
paramMap.put("Signature",str);
return paramMap;
}
测试成功后,突然想用腾讯API进行测试,在项目添加腾讯云api的依赖
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>3.0.29</version>
</dependency>
在文档中找到开发者资源,点击进入页面,填写相关信息,右边代码直接生成代码,自己整理了一下传送的字符串,简直简单到想哭,适合我这种不想深入了解代码的菜鸟
/**
* 文本翻译
*/
@RequestMapping("/textTranslate")
public Result textTranslate(@RequestParam Map<String,String> params){
String sourceText = params.get("content").toString();
String source = params.get("from").toString();
String target = params.get("to").toString();
Integer projectId = 0;
try {
//实例化一个认证对象
Credential cred = new Credential(SECRET_ID,SECRET_KEY);
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("tmt.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
TmtClient client = new TmtClient(cred, "ap-guangzhou", clientProfile);
String sendParams = "{\"SourceText\":\"" + sourceText
+ "\", \"Source\":\"" + source
+ "\",\"Target\":\"" + target
+"\",\"ProjectId\":\"" + projectId + "\"}";
System.out.println("传送的参数:" + sendParams);
TextTranslateRequest req = TextTranslateRequest.fromJsonString(sendParams,TextTranslateRequest.class);
TextTranslateResponse resp = client.TextTranslate(req);
Result result = new Result();
result.setDatas(TextTranslateRequest.toJsonString(resp));
result.setResp_code(0);
result.setResp_msg("翻译成功");
return result;
} catch (TencentCloudSDKException e) {
e.printStackTrace();
return Result.failed("翻译失败");
}
}
一开始咋没发现这个呢,要被自己蠢哭了