JAVA調用騰訊雲API-實現語音合成(TTS)(三)


本節分享給大家通過調用騰訊雲API實現語音合成技術

 

package com.example.combat.controller;

import com.example.combat.service.TTSService;
import com.example.combat.ttsutis.param.TextToVoice;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

/**
 * @description: 語音合成
 * @author:zhucj
 * @date: 2019-11-27 16:37
 */
@RestController
@RequestMapping("/tts")
public class TTSControllerl {

    @Autowired
    private TTSService ttsService;

    @ApiOperation(value = "語音合成")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "text",value = "合成語音的源文本",required = true,dataType = "String"),
            @ApiImplicitParam(name = "volume",value = "音量大小,范圍:[0,10]",required = true,dataType = "String"),
            @ApiImplicitParam(name = "speed",value = "語速,范圍:[-2,2]",required = true,dataType = "String"),
            @ApiImplicitParam(name = "voiceType",value = "音色",required = true,dataType = "Integer")
    })
    @PostMapping(value = "textToVoice")
    public void textToVoice(@Valid @RequestBody TextToVoice textToVoice, HttpServletResponse response){
        ttsService.voiceSynthesis(textToVoice,response);
    }
}
package com.example.combat.service;

import com.example.combat.ttsutis.R;
import com.example.combat.ttsutis.param.TextToVoice;

import javax.servlet.http.HttpServletResponse;

/**
 * @description: 語音合成實現類
 * @author: zhucj
 * @date: 2019-11-27 16:12
 */
public interface TTSService {

    /**
     * 語音合成
     * @param textToVoice
     */
    void voiceSynthesis(TextToVoice textToVoice, HttpServletResponse response);
}
package com.example.combat.service.Impl;

import cn.hutool.core.io.FileUtil;
import com.example.combat.afsutils.Base64ConvertUtils;
import com.example.combat.gaodemapUtils.SystemConstant;
import com.example.combat.service.TTSService;
import com.example.combat.ttsutis.R;
import com.example.combat.ttsutis.TTSUtil;
import com.example.combat.ttsutis.param.TextToVoice;
import com.example.combat.ttsutis.param.TextToVoiceResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Decoder;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Objects;

/**
 * @description:
 * @author: zhucj
 * @date: 2019-11-27 16:15
 */
@Service
@Slf4j
public class TTSServiceImpl implements TTSService {

    @Autowired
    private TTSUtil ttsUtil;

    @Value("${tencent.pathImg}")
    private String pathImg;

    @Override
    public void voiceSynthesis(TextToVoice textToVoice, HttpServletResponse response) {
        R r = ttsUtil.voiceSynthesis(textToVoice);
        if (r.getSuccess() && Objects.equals(r.getCode(), SystemConstant.SUCCESS_CODE)){
            TextToVoiceResponse data =(TextToVoiceResponse) r.getData();
            String audio = data.getAudio();
            //將base64編碼的wav/mp3音頻數據 下載給前端
            exportImg(response,pathImg,audio);
        }else {
            throw new RuntimeException("語音合成失敗");
        }
    }

    public static void exportImg(HttpServletResponse response, String filePath,String imgStr) {

        //對字節數組字符串進行Base64解碼並生成圖片
        String imgFilePath = null;
        //圖像數據為空
        if (imgStr == null){
            return ;
        }
        BASE64Decoder decoder = new BASE64Decoder();
        BufferedOutputStream buff = null;
        ServletOutputStream outStr = null;
        try {
            //Base64解碼
            byte[] b = decoder.decodeBuffer(imgStr);
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {
                    //調整異常數據
                    b[i] += 256;
                }
            }
            response.setCharacterEncoding("utf-8");
            //設置響應的內容類型
            response.setContentType("text/plain");
            //設置文件的名稱和格式
            response.setHeader("content-type", "application/octet-stream");
            response.setContentType("application/octet-stream");
            response.addHeader("Content-Disposition", "attachment;filename=" + "合成語音.wav");
            outStr = response.getOutputStream();
            buff = new BufferedOutputStream(outStr);
            buff.write(b);
            buff.flush();
            buff.close();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件導出異常:", e);
        } finally {
            try {
                buff.close();
            } catch (Exception e) {
                log.error("流關閉異常:", e);
            }
            try {
                outStr.close();
            } catch (Exception e) {
                log.error("流關閉異常:", e);
            }
        }
    }
}
package com.example.combat.ttsutis;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.combat.afsutils.HttpUtil;
import com.example.combat.afsutils.SignUtils;
import com.example.combat.asrutils.param.SentenceRecognitionApi;
import com.example.combat.asrutils.param.SentenceResponse;
import com.example.combat.asrutils.param.SystemConstants;
import com.example.combat.config.constant.ContentTypeEnum;
import com.example.combat.config.constant.HttpMethodEnum;
import com.example.combat.config.constant.SignMenodEnum;
import com.example.combat.ttsutis.param.TextToVoice;
import com.example.combat.ttsutis.param.TextToVoiceResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @description: 語音合成
 * @author: zhucj
 * @date: 2019-11-27 15:35
 */
@Component
@Slf4j
public class TTSUtil {

    @Value("${tencent.secretId}")
    private String sercretId;

    @Value("${tencent.secretKey}")
    private String sercretKey;

    /**
     * 語音合成
     * @param textToVoice
     * @return
     */
   public R voiceSynthesis(TextToVoice textToVoice){

       TreeMap treeMap = createPublicMap("TextToVoice", "2019-08-23", "ap-shenzhen-fsi");
       HashMap<String,Object> hashMap = new HashMap<>();
       try {
           hashMap.put("Text", URLEncoder.encode(textToVoice.getText(),"UTF-8"));
       } catch (UnsupportedEncodingException e) {
          log.error("URL Encoder異常:{}",e.getMessage());
          return R.error("語音合成失敗").setCode(SystemConstants.SERVER_ERROR_CODE);
       }
       hashMap.put("SessionId", UUID.randomUUID().toString());
       hashMap.put("ModelType",1);
       hashMap.put("Volume",Float.valueOf(textToVoice.getVolume()));
       hashMap.put("Speed",Float.valueOf(textToVoice.getSpeed()));
       hashMap.put("VoiceType",textToVoice.getVoiceType());

       //簽名,公共參數不需要放到body中
       String sign = null;
       try {
           sign = SignUtils.sign(treeMap, HttpMethodEnum.POST, SignMenodEnum.TC3_HMAC_SHA256, JSON.toJSONString(hashMap)
                   , TextToVoiceConstant.TEXT_TO_VOICE, sercretKey, ContentTypeEnum.JSON);
       } catch (Exception e) {
           log.error("簽名異常:{}",e.getMessage());
           return R.error("簽名異常").setCode(SystemConstants.SERVER_ERROR_CODE);
       }
       try {
           String respJson = HttpUtil.httpPost(TextToVoiceConstant.TEXT_TO_VOICE, JSON.parseObject(sign, Map.class),hashMap);
           JSONObject jsonObject = JSON.parseObject(respJson);
           String response = jsonObject.getString("Response");
           JSONObject error =(JSONObject) JSON.parseObject(response).get("Error");
           if (Objects.nonNull(error)){
               return R.error(String.valueOf(error.get("Message"))).setCode(SystemConstants.SERVER_ERROR_CODE);
           }else {
               TextToVoiceResponse textToVoiceResponse = JSON.parseObject(response, TextToVoiceResponse.class);
               return R.ok(textToVoiceResponse.getAudio()).setCode(SystemConstants.SUCCESS_CODE);
           }

       } catch (Exception e) {
           log.error("語音合成失敗:{}",e.getMessage());
           return R.error("語音合成失敗").setCode(SystemConstants.SERVER_ERROR_CODE);
       }
   }
    /**
     * 封裝請求公共參數
     * @param action
     * @param version
     * @return
     */
    public TreeMap createPublicMap(String action, String version,String region ){

        TreeMap<String,Object> treeMap = new TreeMap<>();
        treeMap.put("Action",action);
        treeMap.put("Version",version);
        treeMap.put("Region",region);
        treeMap.put("Timestamp",getCurrentTimestamp());
        treeMap.put("Nonce",new Random().nextInt(Integer.MAX_VALUE));
        treeMap.put("SecretId",sercretId);
        return treeMap;
    }

    /**
     * 獲取當前時間戳,單位秒
     * @return
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis()/1000;
    }

}
package com.example.combat.ttsutis;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.ToString;

import java.io.Serializable;

/**
 * 返回類型
 * @author choleece
 * @date 2018/9/27
 */
@ApiModel
@ToString
public class R<T> implements Serializable {

    private static final long serialVersionUID = -6287952131441663819L;

    /**
     * 編碼
     */
    @ApiModelProperty(value = "響應碼", example = "200")
    private int code = 200;

    /**
     * 成功標志
     */
    @ApiModelProperty(value = "成功標志", example = "true")
    private Boolean success;

    /**
     * 返回消息
     */
    @ApiModelProperty(value = "返回消息說明", example = "操作成功")
    private String msg="操作成功";

    /**
     * 返回數據
     */
    @ApiModelProperty(value = "返回數據")
    private T data;

    /**
     * 創建實例
     * @return
     */
    public static R instance() {
        return new R();
    }

    public int getCode() {
        return code;
    }

    public R setCode(int code) {
        this.code = code;
        return this;
    }

    public Boolean getSuccess() {
        return success;
    }

    public R setSuccess(Boolean success) {
        this.success = success;
        return this;
    }

    public String getMsg() {
        return msg;
    }

    public R setMsg(String msg) {
        this.msg = msg;
        return this;
    }

    public T getData() {
        return data;
    }
    public R setData(T data) {
        this.data = data;
        return this;
    }

    public static R ok() {
        return R.instance().setSuccess(true);
    }

    public static R ok(Object data) {
        return ok().setData(data);
    }

    public static R ok(Object data, String msg) {
        return ok(data).setMsg(msg);
    }

    public static R error() {
        return R.instance().setSuccess(false);
    }

    public static R error(String msg) {
        return error().setMsg(msg);
    }

    /**
     * 無參
     */
    public R() {
    }

    public R(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public R(int code, T data){
        this.code = code;
        this.data = data;
    }

    /**
     * 有全參
     * @param code
     * @param msg
     * @param data
     * @param success
     */
    public R(int code, String msg, T data, Boolean success) {
        this.code = code;
        this.msg = msg;
        this.data = data;
        this.success = success;
    }

    /**
     * 有參
     * @param code
     * @param msg
     * @param data
     */
    public R(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

  
}
package com.example.combat.ttsutis;

/**
 * @description: 語音合成常量
 * @author: zhucj
 * @date: 2019-11-27 15:51
 */
public class TextToVoiceConstant {

    /**
     * 語音合成Api
     */
    public static final String TEXT_TO_VOICE = "https://tts.ap-shenzhen-fsi.tencentcloudapi.com";
}
package com.example.combat.ttsutis.param;

import io.swagger.annotations.ApiModel;
import lombok.*;

import javax.validation.constraints.NotNull;

/**
 * @description:
 * @author: zhucj
 * @date: 2019-11-27 15:39
 */
@Data
@ApiModel(description = "語音合成請求實體")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class TextToVoice {


    /**
     * 合成語音的源文本,按UTF-8編碼統一計算。
     * 中文最大支持100個漢字(全角標點符號算一個漢字);
     * 英文最大支持400個字母(半角標點符號算一個字母)。包含空格等字符時需要url encode再傳輸。
     */
    @NotNull(message = "合成語音的源文本不為空")
    private String text;

    /**
     * 音量大小,范圍:[0,10],分別對應11個等級的音量,默認為0
     */
    @NotNull(message = "音量大小不為空")
    private String volume;

    /**
     *語速,范圍:[-2,2],分別對應不同語速:
     * -2代表0.6倍
     * -1代表0.8倍
     * 0代表1.0倍(默認)
     * 1代表1.2倍
     * 2代表1.5倍
     * 輸入除以上整數之外的其他參數不生效,按默認值處理。
     */
    @NotNull(message = "語速大小不為空")
    private String speed;

    /**
     * 音色
     * 0-親和女聲(默認)
     * 1-親和男聲
     * 2-成熟男聲
     * 4-溫暖女聲
     * 5-情感女聲
     * 6-情感男聲
     */
    @NotNull(message = "音色選擇不為空")
    private Integer voiceType;

}
package com.example.combat.ttsutis.param;

import com.example.combat.afsutils.param.resp.Response;
import io.swagger.annotations.ApiModel;
import lombok.*;

/**
 * @description:
 * @author: zhucj
 * @date: 2019-11-27 16:09
 */
@Data
@ApiModel(description = "語音合成響應實體")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class TextToVoiceResponse extends Response {

    private String Audio;

    private String SessionId;
}
package com.example.combat.afsutils;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
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.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class HttpUtil {

    public static final ContentType TEXT_PLAIN = ContentType.create("text/plain", StandardCharsets.UTF_8);

    /**
     * HttpClient 連接池
     */
    private static PoolingHttpClientConnectionManager cm = null;

    static {
        // 初始化連接池,可用於請求HTTP/HTTPS(信任所有證書)
        cm = new PoolingHttpClientConnectionManager(getRegistry());
        // 整個連接池最大連接數
        cm.setMaxTotal(200);
        // 每路由最大連接數,默認值是2
        cm.setDefaultMaxPerRoute(5);
    }

    /**
     * 發送 HTTP GET請求
     * <p>不帶請求參數和請求頭</p>
     * @param url 地址
     * @return
     * @throws Exception
     */
    public static String httpGet(String url) throws Exception {
        log.info("請求參數:{}",url);
        HttpGet httpGet = new HttpGet(url);

        return doHttp(httpGet);
    }

    /**
     * 發送 HTTP GET請求
     * <p>帶請求參數,不帶請求頭</p>
     * @param url    地址
     * @param params 參數
     * @return
     * @throws Exception
     * @throws Exception
     */
    public static String httpGet(String url, Map<String, Object> params) throws Exception {
        // 轉換請求參數
        List<NameValuePair> pairs = covertParams2NVPS(params);

        // 裝載請求地址和參數
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);
        ub.setParameters(pairs);

        HttpGet httpGet = new HttpGet(ub.build());

        return doHttp(httpGet);
    }

    /**
     * 發送 HTTP GET請求
     * <p>帶請求參數和請求頭</p>
     * @param url     地址
     * @param headers 請求頭
     * @param params  參數
     * @return
     * @throws Exception
     * @throws Exception
     */
    public static String httpGet(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
        // 轉換請求參數
        List<NameValuePair> pairs = covertParams2NVPS(params);

        // 裝載請求地址和參數
        URIBuilder ub = new URIBuilder();
        ub.setPath(url);
        ub.setParameters(pairs);

        HttpGet httpGet = new HttpGet(ub.build());
        // 設置請求頭
        for (Map.Entry<String, Object> param : headers.entrySet()){
            httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));}

        return doHttp(httpGet);
    }

    /**
     * 發送 HTTP POST請求
     * <p>不帶請求參數和請求頭</p>
     *
     * @param url 地址
     * @return
     * @throws Exception
     */
    public static String httpPost(String url) throws Exception {
        HttpPost httpPost = new HttpPost(url);

        return doHttp(httpPost);
    }

    /**
     * 發送 HTTP POST請求
     * <p>帶請求參數,不帶請求頭</p>
     *
     * @param url    地址
     * @param params 參數
     * @return
     * @throws Exception
     */
    public static String httpPost(String url, Map<String, Object> params) throws Exception {
        // 轉換請求參數
        List<NameValuePair> pairs = covertParams2NVPS(params);

        HttpPost httpPost = new HttpPost(url);
        // 設置請求參數
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8.name()));

        return doHttp(httpPost);
    }

    /**
     * 發送 HTTP POST請求
     * <p>帶請求參數和請求頭</p>
     *
     * @param url     地址
     * @param headers 請求頭
     * @param params  參數
     * @return
     * @throws Exception
     */
    public static String httpPost(String url, Map<String, Object> headers, Map<String, Object> params) throws Exception {
        log.info("POST請求參數:{}",params);
        HttpPost httpPost = new HttpPost(url);
        // 設置請求參數
        StringEntity entity = new StringEntity(JSON.toJSONString(params),ContentType.APPLICATION_JSON);
        httpPost.setEntity(entity);
        // 設置請求頭
        for (Map.Entry<String, Object> param : headers.entrySet()){
            httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));}

        return doHttp(httpPost);
    }




    /**
     * 轉換請求參數
     *
     * @param params
     * @return
     */
    public static List<NameValuePair> covertParams2NVPS(Map<String, Object> params) {
        List<NameValuePair> pairs = new ArrayList<NameValuePair>();

        for (Map.Entry<String, Object> param : params.entrySet()){
            pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));}

        return pairs;
    }

    /**
     * 發送 HTTP 請求
     *
     * @param request
     * @return
     * @throws Exception
     */
    private static String doHttp(HttpRequestBase request) throws Exception {
        // 通過連接池獲取連接對象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

        return doRequest(httpClient, request);
    }

    /**
     * 發送 HTTPS 請求
     * <p>使用指定的證書文件及密碼</p>
     *
     * @param request
     * @param path
     * @param password
     * @return
     * @throws Exception
     * @throws Exception
     */
    private static String doHttps(HttpRequestBase request, String path, String password) throws Exception {
        // 獲取HTTPS SSL證書
        SSLConnectionSocketFactory csf = getSSLFactory(path, password);
        // 通過連接池獲取連接對象
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();

        return doRequest(httpClient, request);
    }

    /**
     * 獲取HTTPS SSL連接工廠
     * <p>使用指定的證書文件及密碼</p>
     *
     * @param path     證書全路徑
     * @param password 證書密碼
     * @return
     * @throws Exception
     * @throws Exception
     */
    private static SSLConnectionSocketFactory getSSLFactory(String path, String password) throws Exception {

        // 初始化證書,指定證書類型為“PKCS12”
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 讀取指定路徑的證書
        FileInputStream input = new FileInputStream(new File(path));

        try {
            // 裝載讀取到的證書,並指定證書密碼
            keyStore.load(input, password.toCharArray());
        } finally {
            input.close();
        }

        // 獲取HTTPS SSL證書連接上下文
        SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).build();

        // 獲取HTTPS連接工廠,指定TSL版本
        SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());

        return sslCsf;
    }

    /**
     * 獲取HTTPS SSL連接工廠
     * <p>跳過證書校驗,即信任所有證書</p>
     *
     * @return
     * @throws Exception
     */
    private static SSLConnectionSocketFactory getSSLFactory() throws Exception {
        // 設置HTTPS SSL證書信息,跳過證書校驗,即信任所有證書請求HTTPS
        SSLContextBuilder sslBuilder = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        });

        // 獲取HTTPS SSL證書連接上下文
        SSLContext sslContext = sslBuilder.build();

        // 獲取HTTPS連接工廠
        SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);

        return sslCsf;
    }

    /**
     * 獲取 HTTPClient注冊器
     *
     * @return
     * @throws Exception
     */
    private static Registry<ConnectionSocketFactory> getRegistry() {
        Registry<ConnectionSocketFactory> registry = null;

        try {
            registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", new PlainConnectionSocketFactory()).register("https", getSSLFactory()).build();
        } catch (Exception e) {
            log.error("獲取 HTTPClient注冊器失敗", e);
        }

        return registry;
    }


    /**
     * 處理Http/Https請求,並返回請求結果
     * <p>注:默認請求編碼方式 UTF-8</p>
     *
     * @param httpClient
     * @param request
     * @return
     * @throws Exception
     */
    private static String doRequest(CloseableHttpClient httpClient, HttpRequestBase request) throws Exception {
        String result = null;
        CloseableHttpResponse response = null;

        try {
            // 獲取請求結果
            response = httpClient.execute(request);
            // 解析請求結果
            HttpEntity entity = response.getEntity();
            // 轉換結果
            result = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());
            // 關閉IO流
            EntityUtils.consume(entity);
        } finally {
            if (null != response){
                response.close();}
        }

        return result;
    }
}
package com.example.combat.afsutils;

import com.alibaba.fastjson.JSON;
import com.example.combat.config.constant.ContentTypeEnum;
import com.example.combat.config.constant.HttpMethodEnum;
import com.example.combat.config.constant.SignMenodEnum;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import java.util.TimeZone;
import java.util.TreeMap;

/**
 * @description: 騰訊雲 簽名方法
 * @author: zhucj
 * @date: 2019-10-18 14:14
 */
@Slf4j
public class SignUtils {


    private final static String CHARSET = "UTF-8";

    private final static Charset UTF8 = StandardCharsets.UTF_8;




    public static String sign(TreeMap<String, Object> params, HttpMethodEnum menth, SignMenodEnum signMenodEnum,
                              String jsonString, String reqUrl, String sercretKey, ContentTypeEnum typeEnum) throws Exception {
        String signString = null;
        String  sercretId  = String.valueOf(params.get("SecretId"));

        switch (signMenodEnum){
            case TC3_HMAC_SHA256:
                String replace = reqUrl.replace("https://", "");
                String service = replace.substring(0,3);
                String host = replace;
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                // 注意時區,否則容易出錯
                sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
                String date = sdf.format(new Date(Long.valueOf(params.get("Timestamp") + "000")));
                // ************* 步驟 1:拼接規范請求串 *************
                String canonicalUri = "/";
                String canonicalQueryString = "";
                String canonicalHeaders = "content-type:"+typeEnum.getName() +"\n" + "host:" + host + "\n";
                String signedHeaders = "content-type;host";

                String hashedRequestPayload = sha256Hex(jsonString);
                String canonicalRequest = menth.getName() + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
                        + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;

                // ************* 步驟 2:拼接待簽名字符串 *************
                String credentialScope = date + "/" + service + "/" + "tc3_request";
                String hashedCanonicalRequest = sha256Hex(canonicalRequest);
                String stringToSign = signMenodEnum.getMendoName() + "\n" + params.get("Timestamp") + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
                log.info("待簽名參數:{}",stringToSign);

                // ************* 步驟 3:計算簽名 *************
                byte[] secretDate = hmac256(("TC3" +sercretKey).getBytes(UTF8), date);
                byte[] secretService = hmac256(secretDate, service);
                byte[] secretSigning = hmac256(secretService, "tc3_request");
                String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
                log.info("生成簽名參數:{}",signature);

                // ************* 步驟 4:拼接 Authorization *************
                String authorization = signMenodEnum.getMendoName() + " " + "Credential=" + sercretId + "/" + credentialScope + ", "
                        + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
                log.info("生成authorization參數:{}",authorization);

                TreeMap<String, String> headers = new TreeMap<String, String>();
                headers.put("Authorization", authorization);
                headers.put("Content-Type",typeEnum.getName());
                headers.put("Host", host);
                headers.put("X-TC-Action",String.valueOf(params.get("Action")) );
                headers.put("X-TC-Timestamp",String.valueOf(params.get("Timestamp")));
                headers.put("X-TC-Version",String.valueOf(params.get("Version")));
                if (Objects.nonNull(params.get("Region"))){
                    headers.put("X-TC-Region",String.valueOf(params.get("Region")));
                }
                signString = JSON.toJSONString(headers);

                break;
                default:
                    StringBuilder s2s = new StringBuilder(reqUrl.replace("https://",menth.getName())+"/?");
                    // 簽名時要求對參數進行字典排序,此處用TreeMap保證順序
                    for (String k : params.keySet()) {
                        s2s.append(k).append("=").append(params.get(k).toString()).append("&");
                    }
                    String s = s2s.toString().substring(0, s2s.length() - 1);
                    Mac mac = Mac.getInstance(signMenodEnum.getMendoName());
                    SecretKeySpec secretKeySpec = new SecretKeySpec(sercretKey.getBytes(CHARSET), mac.getAlgorithm());
                    mac.init(secretKeySpec);
                    byte[] hash = mac.doFinal(s.getBytes(CHARSET));
                    signString = DatatypeConverter.printBase64Binary(hash);
                    break;
        }
        return signString ;


    }

    /**
     * 獲取簽名之后的請求Url
     * @param params
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String getUrl(TreeMap<String, Object> params,String reqUrl) throws UnsupportedEncodingException {
        StringBuilder url = new StringBuilder(reqUrl+"/?");
        // 實際請求的url中對參數順序沒有要求
        for (String k : params.keySet()) {
            // 需要對請求串進行urlencode,由於key都是英文字母,故此處僅對其value進行urlencode
            url.append(k).append("=").append(URLEncoder.encode(params.get(k).toString(), CHARSET)).append("&");
        }
        return url.toString().substring(0, url.length() - 1);
    }

    public static String getUrl(TreeMap<String, Object> params,String reqUrl,String jsonString,ContentTypeEnum typeEnum){
        String replace = reqUrl.replace("https://", "");
        StringBuilder sb = new StringBuilder();
        sb.append("curl -X POST https://").append(replace)
                .append(" -H \"Authorization: ").append(params.get("Authorization")).append("\"")
                .append(" -H \"Content-Type:").append(typeEnum.getName())
                .append(" -H \"Host: ").append(replace).append("\"")
                .append(" -H \"X-TC-Action: ").append(params.get("Action")).append("\"")
                .append(" -H \"X-TC-Timestamp: ").append(params.get("Timestamp")).append("\"")
                .append(" -H \"X-TC-Version: ").append(params.get("Version")).append("\"");
        if (Objects.nonNull(params.get("Region"))){
            sb.append(" -H \"X-TC-Region: ").append(params.get("Region")).append("\"");
        }
        sb.append(" -d '").append(jsonString).append("'");
        return sb.toString();
    }


    public static byte[] hmac256(byte[] key, String msg) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
        mac.init(secretKeySpec);
        return mac.doFinal(msg.getBytes(UTF8));
    }


    public static String sha256Hex(String s) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] d = md.digest(s.getBytes(UTF8));
        return DatatypeConverter.printHexBinary(d).toLowerCase();
    }


}
#騰訊雲服務配置
tencent:
  secretId: *******
  secretKey: *******
  pathImg: E:/upload/

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM