okhttp3 请求服务器返回流式数据

package com.abc

import android.util.Log
import com.alibaba.fastjson.JSONArray
import com.alibaba.fastjson.JSONException
import com.alibaba.fastjson.JSONObject
import com.uurobot.voicesrc.interfaces.IChatUtil
import com.uurobot.voicesrc.utils.SynonymsUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Interceptor
import okhttp3.Interceptor.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException


/**
 *   百度文档
 *   https://developer.dueros.baidu.com/doc/dueros-conversational-service/device-interface/catalog.md
 *
 */
class BaiduChatUtil3 : IChatUtil() {
    private val client = OkHttpClient.Builder().callTimeout(20, TimeUnit.SECONDS)
        .addInterceptor(RetryInterceptor(5, 200)).build()
    private var call: Call? = null
    private var accessToken = ""
    private var scope: CoroutineScope = CoroutineScope(Dispatchers.Default);

    override fun cancel() {
        call?.cancel()
    }

    init {
        scope.launch {
            while (true) {
                try {
                    accessToken = getAccessToken();
                } catch (e: IOException) {
                    e.printStackTrace();
                }
                delay(60_000)
            }
        }
    }

    //    @Override
    fun getNlpKey(mac: String?) {
        SynonymsUtil.getMacInfo(mac)
    }

    private suspend fun getAccessToken(): String {
        return suspendCancellableCoroutine {
            val request: Request = Request.Builder()
                .url("https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET")
                .build()
            call = client.newCall(request)
            call!!.enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    e.printStackTrace()
                    it.resumeWithException(e)
                }

                @Throws(IOException::class)
                override fun onResponse(call: Call, response: Response) {
                    if (!response.isSuccessful) {
                        it.resumeWithException(IOException(response.body?.string()))
                        return
                    }
                    val result = response.body!!.string()
                    val jsonObject = JSONObject.parseObject(result)
//                    Log.e(TAG, "andy0612 access_token :  $result")
                    val accessToken = jsonObject.getString("access_token")
                    it.resume(accessToken)
                    response.close()

                }
            })
        }
    }

    private fun onChat2(chat: String, accessToken: String) {
        Log.e(TAG, "onChat2 :  $chat")
        val json = JSONObject()
        try {
            json["pid"] = "101"
            json["qid"] = "123ddcv"
            json["userId"] = USER_ID

            // 构造请求体
            val data = JSONObject()
            data["prompt"] = chat
            data["stream"] = true
            data["max_tokens"] = 200
            json["data"] = data

            // 构造请求体
            val dcs_data = JSONObject()
            dcs_data["cuid"] = USER_ID + "002"
            dcs_data["sn"] = USER_ID + "10"
            dcs_data["didp"] = "11"
            dcs_data["events"] = EventsData.getString();
            json["dcs_data"] = dcs_data

        } catch (e: JSONException) {
            e.printStackTrace()
        }
        val a = json.toJSONString()
        Log.e(TAG, "andy0612 request :  $a")
        val body = a.toRequestBody("application/json; charset=utf-8".toMediaType())
        val request: Request = Request.Builder()
            //https://duer-kids.baidu.com/botwatch/api/operation/aigc/stream/dcschat
            .url("https://duer-kids.baidu.com/botwatch/api/operation/aigc/stream/dcschat")
            .post(body)
            .addHeader("Client-Id", CLIENT_ID) // 添加 Client-Id 头字段
            .addHeader("Authorization", "Bearer $accessToken")

            .build()
        call = client.newCall(request)
        call!!.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.e(TAG, "request onFailure :  ${e.message}")
                onError(e.toString())
                e.printStackTrace();
            }

            @Throws(IOException::class)
            override fun onResponse(call: Call, response: Response) {
                // 获取响应的ResponseBody
                val responseBody = response.body
                // 获取InputStream进行流式读取
                val inputStream = responseBody!!.byteStream()
                // 创建一个缓冲区用于读取数据
                val buffer = ByteArray(8192 * 4)
                var bytesRead: Int
                val outputStream2 = ByteArrayOutputStream()
                var isDcs = false;

                try {
                    // 循环读取流数据
                    while (inputStream.read(buffer).also { bytesRead = it } != -1) {
                        Log.i(TAG, "andy0716 bytesRead:$bytesRead");
                        if (isDcs) {
                            outputStream2.write(buffer, 0, bytesRead)
                            continue
                        }

                        val str = String(buffer, 0, bytesRead, StandardCharsets.UTF_8);
                        Log.i(TAG, "andy0716 stream read:$str");
                        if (str.startsWith("data:")) {
                            val result = str.substring(6)
                            Log.i(TAG, "andy0716 stream result:$result");
                            if (result.startsWith("{\"type\":\"dcs")) {
                                isDcs = true;
                                outputStream2.write(buffer, 0, bytesRead)
                                Log.i(TAG, "andy0716 stream continue");
                                continue;
                            }

                            val jsonObject = JSONObject.parseObject(result.trimIndent())
                            val data = jsonObject.getJSONObject("data");
                            val ended = jsonObject.getInteger("ended");
                            val index = data.getInteger("index");
                            Log.i(TAG, "andy0716 index:$index, ended:$ended");
                            val part = data.getString("part");
                            Log.i(TAG, "andy0716 part:$part");
                            if(part.trim().isNotEmpty()){
                                if (index == 0) {
                                    onGetTextContent(part, "")
                                } else {
                                    onGetNextText(part)
                                    if (ended == 1) {
                                        return
                                    }
                                }
                            }
                            else {
                                Log.i(TAG, "andy0716 part is null");
                            }

                        }
                    }
                    if (isDcs) {
                        val result = outputStream2.toByteArray().toString(StandardCharsets.UTF_8).substring(6)
                        Log.e(TAG, "andy0716 dcs result -> $result")
                        val data =
                            JSONObject.parseObject(result).getJSONObject("data")
                        val dcs: JSONArray = data.getJSONArray("dcs")
                        if (dcs.isEmpty()) {
                            val tts = data.getJSONObject("tts").getString("text")
                            Log.e(TAG, "tts -> $tts")
                            onGetTextContent(tts, "")
                            return
                        }

                        val tts = data.getJSONObject("tts").getString("text")
                        for (index in 0 until dcs.size) {
                            val directive = dcs.getJSONObject(index).getJSONObject("directive")
                            val headerName = directive.getJSONObject("header").getString("name")
                            if (headerName == "Play") {
                                val url = directive.getJSONObject("payload")
                                    .getJSONObject("audioItem").getJSONObject("stream")
                                    .getString("url");
                                Log.e(TAG, "andy0612 url -> $url")
                                Log.e(TAG, "andy0612 tts -> $tts")
                                onGetVoiceContent(tts, url)
                                return
                            }
                        }
                        Log.e(TAG, "tts -> $tts")
                        onGetTextContent(tts, "")
                        return
                    }

                } catch (e: IOException) {
                    e.printStackTrace()
                } finally {
                    inputStream.close()
                    response.close()
                }
            }
        })
    }

    override fun onChat(chat: String) {
        if (accessToken == "") {
            scope.launch {
                try {
                    accessToken = getAccessToken();
                } catch (e: IOException) {
                    e.printStackTrace();
                }
//                Log.e(TAG, "andy0612 getAccessToken:   $accessToken")
                onChat2(chat, accessToken);
            }
        } else {
            onChat2(chat, accessToken);
        }

    }

    companion object {
        const val TAG = "andy0715 BaiduChatUtil"
        const val CLIENT_ID = "CkWnW111112www"
        const val CLIENT_SECRET = "222222222222222222"
        val USER_ID = "canbot_001"
    }

    class RetryInterceptor(private val maxAttempts: Int, private val retryInterval: Long) :
        Interceptor {
        @Throws(IOException::class)
        override fun intercept(chain: Interceptor.Chain): Response {
            val request: Request = chain.request()
            var attempt = 0
            var response: Response? = null
            while (attempt < maxAttempts) {
                try {
                    Log.e(TAG, "before chain.proceed");
                    response?.close()
                    response = chain.proceed(request)
                    Log.e(TAG, "after chain.proceed");
                    if (response.isSuccessful) {
                        return response
                    }
                } catch (e: IOException) {
                    response?.close()
                    // 请求失败,等待重试间隔时间后进行下一次重试
                    if (attempt == maxAttempts - 1) {
                        throw e
                    }
                    try {
                        Thread.sleep(retryInterval)
                    } catch (ex: InterruptedException) {
                        Thread.currentThread().interrupt()
                        throw IOException("Retry interrupted", ex)
                    }
                }
                attempt++
            }
            return response!!
        }
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值