Java中使用OkHttp進(jìn)行Http通信

提供一個(gè)OkHttp的工具類方法谎亩。

1. 引入依賴

<!-- 通信 -->
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.8.1</version>
</dependency>
<!-- 序列化方式-->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
</dependency>

2. 工具類代碼

工具類需要?jiǎng)?chuàng)建一個(gè)單例的OkHttp的客戶端。

import com.google.gson.Gson;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


public class OkHttpUtil {
    public final static Logger logger = LoggerFactory.getLogger(OkHttpUtil.class);
    /**
     * 最大連接時(shí)間
     */
    public final static int CONNECTION_TIMEOUT = 5;
    /**
     * JSON格式
     */
    public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
    /**
     * OkHTTP線程池最大空閑線程數(shù)
     */
    public final static int MAX_IDLE_CONNECTIONS = 100;
    /**
     * OkHTTP線程池空閑線程存活時(shí)間
     */
    public final static long KEEP_ALIVE_DURATION = 30L;
    /**
     * GSON格式
     */
    public static final Gson GSON = new Gson();

    public static String BASE64_PREFIX = "data:image/png;base64,";

    /**
     * client
     * 配置重試
     */
    private final static OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
            .connectionPool(new ConnectionPool(MAX_IDLE_CONNECTIONS, KEEP_ALIVE_DURATION, TimeUnit.MINUTES))
            .build();


    /**
     * get請(qǐng)求涧尿,無(wú)需轉(zhuǎn)換對(duì)象
     *
     * @param url     鏈接
     * @param headers 請(qǐng)求頭
     * @return 響應(yīng)信息
     */
    public static String get(String url, Map<String, String> headers) {
        try {
            Request.Builder builder = new Request.Builder();
            buildHeader(builder, headers);
            Request request = builder.url(url).build();
            Response response = HTTP_CLIENT.newCall(request).execute();

            if (response.isSuccessful() && Objects.nonNull(response.body())) {
                String result = response.body().string();
                logger.info("執(zhí)行g(shù)et請(qǐng)求, url: {} 成功宽闲,返回?cái)?shù)據(jù): {}", url, result);
                return result;
            }
        } catch (Exception e) {
            logger.error("執(zhí)行g(shù)et請(qǐng)求肃廓,url: {} 失敗!", url, e);
        }
        return null;
    }


    /**
     * Form表單提交
     *
     * @param url    地址
     * @param params form參數(shù)
     * @return
     */
    public static String post(String url, Map<String, String> params) {
        try {

            FormBody.Builder builder = new FormBody.Builder();
            if (!CollectionUtils.isEmpty(params)) {
                params.forEach(builder::add);
            }
            FormBody body = builder.build();
            Request request = new Request.Builder().url(url).post(body).build();
            Response response = HTTP_CLIENT.newCall(request).execute();
            //調(diào)用成功
            if (response.isSuccessful() && response.body() != null) {
                return response.body().string();
            }
        } catch (Exception e) {
            logger.error("", e);
        }
        return null;
    }


    /**
     * 簡(jiǎn)單post請(qǐng)求
     *
     * @param url     請(qǐng)求url
     * @param headers 請(qǐng)求頭
     * @param json    請(qǐng)求參數(shù)
     * @return
     */
    public static String post(String url, Map<String, String> headers, String json) {
        try {
            RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, json);
            Request.Builder builder = new Request.Builder();
            buildHeader(builder, headers);
            Request request = builder.url(url).post(body).build();
            Response response = HTTP_CLIENT.newCall(request).execute();
            if (response.isSuccessful() && Objects.nonNull(response.body())) {
                String result = response.body().string();
                logger.info("執(zhí)行post請(qǐng)求,url: {}, header: {} ,參數(shù): {} 成功挎峦,返回結(jié)果: {}", url, headers, json, result);
                return result;
            }
        } catch (Exception e) {
            logger.error("執(zhí)行post請(qǐng)求,url: {},參數(shù): {} 失敗!", url, json, e);
        }
        return null;
    }


    /**
     * 設(shè)置請(qǐng)求頭
     *
     * @param builder .
     * @param headers 請(qǐng)求頭
     */
    private static void buildHeader(Request.Builder builder, Map<String, String> headers) {
        if (Objects.nonNull(headers) && headers.size() > 0) {
            headers.forEach((k, v) -> {
                if (Objects.nonNull(k) && Objects.nonNull(v)) {
                    builder.addHeader(k, v);
                }
            });
        }
    }


    /**
     * 支持嵌套泛型的post請(qǐng)求搜吧。
     * <pre>
     *   Type type = new TypeToken<Results<User>>() {}.getType();
     * <pre/>
     *
     * @param url     鏈接
     * @param headers 請(qǐng)求頭
     * @param json    請(qǐng)求json
     * @param type    嵌套泛型
     * @return 響應(yīng)對(duì)象, 可進(jìn)行強(qiáng)轉(zhuǎn)市俊。
     */
    public static <T> T post(String url, Map<String, String> headers, String json, Type type) {
        String result = post(url, headers, json);
        if (Objects.nonNull(result) && Objects.nonNull(type)) {
            return GSON.fromJson(result, type);
        }
        return null;
    }
    /**
     * 讀取流,轉(zhuǎn)換為Base64
     * 返回base64的照片
     */
    public static String postImg(String url, Map<String, String> headers, String json) {
        RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, json);
        Request.Builder builder = new Request.Builder();
        buildHeader(builder, headers);
        Request request = builder.url(url).post(body).build();
        try {
            Response response = HTTP_CLIENT.newCall(request).execute();
            String contentType = null;
            if (response.body() != null && response.body().contentType() != null) {
                contentType = response.body().contentType().toString();
            }
            if ("image/png".equals(contentType)) {
                //讀取流
                return BASE64_PREFIX + new String(Base64.getEncoder().encode(response.body().bytes()));
            }
        } catch (IOException e) {
            logger.error("", e);
        }
        return null;
    }


    /**
     * 異步獲取照片地址
     */
    public static void downloadImg(String url, String json) {
        RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, json);

        Request request = new Request.Builder().url(url).post(body).build();

        Call call = HTTP_CLIENT.newCall(request);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //打印異常
                logger.error("", e);
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String s = BASE64_PREFIX + new String(Base64.getEncoder().encode(response.body().bytes()));
                logger.info(s);
            }
        });

    }
}

3. 注意

3.1 Response讀取響應(yīng)結(jié)果

可以通過(guò)Response的code來(lái)判斷請(qǐng)求是否成功滤奈,如果服務(wù)器返回的有數(shù)據(jù)摆昧,可以通過(guò)Response的body得到一個(gè)ResponseBody讀取。

  • response.body().string()一次性將響應(yīng)體的信息全部讀取并轉(zhuǎn)換為String格式蜒程;
  • response.body().bytes()一次性讀取響應(yīng)體的信息到byte[]數(shù)組中绅你。

如果數(shù)據(jù)超過(guò)1MB可能會(huì)報(bào)內(nèi)存溢出,所以對(duì)于超過(guò)1MB的數(shù)據(jù)昭躺,建議采用流的方式去讀取忌锯,如ResponseBody的byteStream()方法。

OkHttp調(diào)用微信小程序二維碼接口照片是黑屏

附錄

  • 被調(diào)用者代碼
import com.alibaba.fastjson.JSON;
import com.tellme.pojo.Results;
import com.tellme.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

@Slf4j
@RestController
public class TestOkHttp {

    /**
     * get請(qǐng)求
     */
    @GetMapping("/test/server")
    public String getMsg() {
        return "這是OkHttp的請(qǐng)求";
    }

    /**
     * form表單
     */
    @PostMapping("/test/form")
    public String getFormMsg(User user) {
        log.info(JSON.toJSONString(user));
        return "這是Form的請(qǐng)求" + JSON.toJSONString(user);
    }

    /**
     * post請(qǐng)求
     */
    @PostMapping("/test/user")
    public User getUser(@RequestBody User user) {
        log.info(JSON.toJSONString(user));
        return user;
    }

    /**
     * post請(qǐng)求
     */
    @PostMapping("/test/result")
    public Results getResult(@RequestBody User user) {
        Results<User> results = new Results<>();
        results.setCode(200);
        results.setStatus("OK");
        results.setData(user);
        return results;
    }

    /**
     * 返回照片
     */
    @PostMapping("/test/img")
    public void getResult(HttpServletRequest request,
                          HttpServletResponse response) {
        try {
            File file = new File("/Users/yexuerui/Downloads/二維碼池改造.png");
            FileInputStream inputStream = new FileInputStream(file);
            byte[] data = new byte[(int) file.length()];
            //信息從文件輸入到JVM的數(shù)組中
            inputStream.read(data);
            inputStream.close();
            response.setContentType("image/png");
            ServletOutputStream outputStream = response.getOutputStream();
            //信息從JVM的數(shù)組輸出到
            outputStream.write(data);
            outputStream.close();
        } catch (FileNotFoundException e) {
            log.error("", e);
        } catch (IOException e) {
            log.error("", e);
        }
    }
}
  • 調(diào)用者代碼
@Slf4j
@RestController
public class TestOkHttpController {


    @GetMapping("/test/okHttp")
    public String sendMsg() {
        String url = "http://localhost:8010/test/server";
        Map<String, String> header = new HashMap<>();
        return OkHttpUtil.get(url, header);
    }

    @GetMapping("/test/form")
    public String sendForm() {
        String url = "http://localhost:8010/test/form";
        Map<String, String> params = new HashMap<>();
        params.put("name", "libai");
        params.put("age", "1");
        return OkHttpUtil.post(url, params);
    }


    @GetMapping("/test/user")
    public String sendUser() {
        String url = "http://localhost:8010/test/user";
        User user=new User();
        user.setName("平安");
        user.setAge(12);
        return OkHttpUtil.post(url, null, JSON.toJSONString(user));
    }

    //帶有嵌套泛型的對(duì)象
    @GetMapping("/test/result")
    public void sendResult() {
        String url = "http://localhost:8010/test/result";
        User user=new User();
        user.setName("平安");
        user.setAge(12);

        Results<User> results = OkHttpUtil.post(url, null, JSON.toJSONString(user), new TypeToken<Results<User>>() {
        }.getType());
        log.info(JSON.toJSONString(results));
    }


    @GetMapping("/test/img")
    public String sendImg() {
        String url = "http://localhost:8010/test/img";
        return OkHttpUtil.postImg(url, null, "{}");
    }
    //異步調(diào)用
    @GetMapping("/test/sync/img")
    public void sendSyncImg() {
        String url = "http://localhost:8010/test/img";
        OkHttpUtil.downloadImg(url,  "{}");
        log.info("請(qǐng)求結(jié)束");
    }
}

推薦閱讀

重識(shí)OkHttp——更深入了解如何使用

OkHttp使用詳解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末领炫,一起剝皮案震驚了整個(gè)濱河市偶垮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帝洪,老刑警劉巖似舵,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異葱峡,居然都是意外死亡砚哗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)砰奕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)频祝,“玉大人泌参,你說(shuō)我怎么就攤上這事脆淹〕?眨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵盖溺,是天一觀的道長(zhǎng)漓糙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)烘嘱,這世上最難降的妖魔是什么昆禽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蝇庭,結(jié)果婚禮上醉鳖,老公的妹妹穿的比我還像新娘。我一直安慰自己哮内,他們只是感情好盗棵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著北发,像睡著了一般纹因。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上琳拨,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天瞭恰,我揣著相機(jī)與錄音,去河邊找鬼狱庇。 笑死惊畏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的密任。 我是一名探鬼主播颜启,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼批什!你這毒婦竟也來(lái)了农曲?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤驻债,失蹤者是張志新(化名)和其女友劉穎乳规,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體合呐,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡暮的,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了淌实。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冻辩。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猖腕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恨闪,到底是詐尸還是另有隱情倘感,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布咙咽,位于F島的核電站老玛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏钧敞。R本人自食惡果不足惜蜡豹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望溉苛。 院中可真熱鬧镜廉,春花似錦、人聲如沸愚战。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)凤巨。三九已至视乐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敢茁,已是汗流浹背佑淀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彰檬,地道東北人伸刃。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像逢倍,于是被迫代替她去往敵國(guó)和親捧颅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容