All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.net.wanmo.common.http.jdk.HttpJdkUtil Maven / Gradle / Ivy

There is a newer version: 1.1.1
Show newest version
package cn.net.wanmo.common.http.jdk;

import cn.net.wanmo.common.charset.CharsetUtil;
import cn.net.wanmo.common.codec.CodecUtil;
import cn.net.wanmo.common.http.jdk.enums.Header;
import cn.net.wanmo.common.http.jdk.enums.Method;
import cn.net.wanmo.common.http.jdk.pojo.ResData;
import cn.net.wanmo.common.http.jdk.pojo.ResObj;
import cn.net.wanmo.common.http.jdk.pojo.UploadFile;
import cn.net.wanmo.common.http.jdk.pojo.Uploader;
import cn.net.wanmo.common.http.jdk.ssl.NullHostNameVerifier;
import cn.net.wanmo.common.http.jdk.ssl.SSLContextFactory;
import cn.net.wanmo.common.http.jdk.util.ResUtil;
import cn.net.wanmo.common.result.HttpResult;
import cn.net.wanmo.common.util.IdGen;
import cn.net.wanmo.common.util.MapUtil;
import cn.net.wanmo.common.util.StringUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * HTTP 请求
 */
public class HttpJdkUtil {
    private final Logger logger = LoggerFactory.getLogger(getClass());


    private final String CRLF = "\r\n";
    private final String TWO_HYPHEN = "--";
    private final String BOUNDARY = "----WebKitFormBoundary" + IdGen.uuid();

    /**
     * 创建请求
     */
    private static HttpJdkUtil create() {
        HttpJdkUtil httpJdkUtil = new HttpJdkUtil();
        return httpJdkUtil;
    }

    /**
     * 创建请求
     */
    public static HttpJdkUtil get(String url) {
        return get(url, true);
    }

    /**
     * 创建请求
     */
    public static HttpJdkUtil get(String url, boolean isUrlEncode) {
        HttpJdkUtil req = create();
        req.method = Method.GET;
        req.url = url;
        req.isUrlEncode = isUrlEncode; // 默认对请求参数进行 URL 编码

        return req;
    }


    /**
     * 创建请求
     */
    public static HttpJdkUtil post(String url) {
        return post(url, false);
    }

    /**
     * 创建请求
     */
    public static HttpJdkUtil post(String url, boolean isUrlEncode) {
        HttpJdkUtil req = create();
        req.method = Method.POST;
        req.url = url;
        req.isUrlEncode = isUrlEncode; // 默认对请求参数不进行 URL 编码

        return req;
    }

    private void processGetUrl() {
        if (this.method != Method.GET) {
            return;
        }

        if (this.params == null || this.params.isEmpty()) {
            return;
        }

        String s = toParamStr(this.params);

        if (this.url.contains("?")) {
            this.url = this.url + "&" + s;
        } else {
            this.url = this.url + "?" + s;
        }
    }

    /**
     * 发送请求
     */
    public HttpResult send() {
        return send(false);
    }

    /**
     * 发送请求
     */
    public HttpResult send(boolean download) {
        processGetUrl(); // 处理 get 请求 url 追加参数

        return getResData(download);
    }

    /**
     * 发送请求
     */
    public  HttpResult> send(Obj obj) {
        processGetUrl(); // 处理 get 请求 url 追加参数

        HttpResult> resReturn = new HttpResult<>();

        {
            HttpResult resTemp = getResData(false);

            {
                resReturn.setCode(resTemp.getCode());
                resReturn.setMsg(resTemp.getMsg());
                resReturn.setStartTime(resTemp.getStartTime());
                resReturn.setConsumeTime(resTemp.getConsumeTime());
                resReturn.setDesc(resTemp.getDesc());
            }

            if (resTemp.isSuccess()) { // 如果数据响应成功,则处理数据
                ResData data = resTemp.getData();
                obj.parse(data.getBody());
                data.setObj(obj);

                resReturn.setData(data);
            }
        }

        return resReturn;
    }

    // ---------------------===============---------------------------
    /**
     * 请求URL地址
     */
    private String url;
    /**
     * 设置连接主机超时
     */
    private Integer connectTimeout = 3000;
    /**
     * 设置从主机读取数据超时
     */
    private Integer readTimeout = 10000;
    /**
     * 响应解析,默认编码
     */
    private Charset charset = CharsetUtil.UTF8;
    /**
     * 请求方式
     */
    private Method method = Method.GET;
    /**
     * 请求头信息
     */
    private Map header = new HashMap<>();
    /**
     * 请求参数
     */
    private Map params = new HashMap<>();
    /**
     * 上传文件
     */
    private Map uploaders = new HashMap<>();
    /**
     * 请求体
     */
    private String body = StringUtil.EMPTY;
    /**
     * 是否对参数 urlEncode
     */
    private boolean isUrlEncode = false;
    /**
     * 是否 multipart
     */
    private boolean isMultipart = false;

    /**
     * HTTPS 服务器证书文件
     */
    private String certFilePath = "";
    /**
     * HTTPS 服务器证书密码
     */
    private String certFilePassword = "";

    public HttpJdkUtil() {
    }

    public HttpJdkUtil connectTimeout(Integer connectTimeout) {
        this.connectTimeout = connectTimeout;
        return this;
    }

    public HttpJdkUtil readTimeout(Integer readTimeout) {
        this.readTimeout = readTimeout;
        return this;
    }


    public HttpJdkUtil charset(Charset charset) {
        this.charset = charset;
        return this;
    }


    public HttpJdkUtil header(Map header) {
        if (header == null || header.isEmpty()) {
            return this;
        }

        for (Map.Entry entry : header.entrySet()) {
            header(entry.getKey(), entry.getValue());
        }

        return this;
    }

    public HttpJdkUtil header(String name, Object value) {
        this.header.put(name, String.valueOf(value));
        return this;
    }

    public HttpJdkUtil isUrlEncode(boolean isUrlEncode) {
        this.isUrlEncode = isUrlEncode;
        return this;
    }

    public HttpJdkUtil form(String name, Object value) {
        if (isUrlEncode) {
            value = CodecUtil.urlEncode(String.valueOf(value));
        }

        this.params.put(name, value);
        return this;
    }

    public HttpJdkUtil form(String name, File file) {
        return form(name, file, file.getName());
    }

    public HttpJdkUtil form(String name, File file, String filename) {
        String digest;
        try {
            FileInputStream is = new FileInputStream(file);
            digest = DigestUtils.md5Hex(is);
        } catch (Exception e) {
            digest = "";
            logger.warn("生成文件默认签名值异常", e);
        }

        return form(name, file, filename, digest);
    }

    /**
     * 添加附件
     *
     * @param name     表单 name
     * @param file     文件
     * @param filename 文件名称
     * @param digest   文件签名
     * @return this
     */
    public HttpJdkUtil form(String name, File file, String filename, String digest) {
        Uploader uploader = uploaders.getOrDefault(name, new Uploader());

        uploader.setName(name);
        {
            UploadFile uploadFile = new UploadFile();
            uploadFile.setFile(file);
            uploadFile.setFilename(filename);
            uploadFile.setDigest(digest);

            uploader.getFiles().add(uploadFile);
        }
        uploaders.put(name, uploader);

        this.isMultipart = true;
        return this;

    }

    public HttpJdkUtil body(String body) {
        this.body = body;

        // 代表发送端(客户端|服务器)发送的实体数据的数据类型
        this.header.put(Header.CONTENT_TYPE.getValue(), "application/json; charset=" + charset.name());

        return this;
    }

    public HttpJdkUtil certFilePath(String certFilePath) {
        this.certFilePath = certFilePath;
        return this;
    }

    public HttpJdkUtil certFilePassword(String certFilePassword) {
        this.certFilePassword = certFilePassword;
        return this;
    }


// ---------------------======= 发起请求 解析响应 ========---------------------------

    /**
     * 发起 HTTP 请求并获取结果
     */
    private HttpResult getResData(boolean download) {
        HttpResult result = new HttpResult();

        HttpURLConnection conn = null;
        try {
            conn = getConnection();
            result.setCode(conn.getResponseCode());
            result.setMsg("Http请求完成");
            ResData data = new ResData<>(conn.getHeaderFields());
            result.setData(data);

            InputStream is;
            if (result.isSuccess()) {
                logger.debug("处理成功响应...");
                is = conn.getInputStream(); // 请求后远程返回的数据

                String contentType = data.getHeaders().get(Header.CONTENT_TYPE.getValue()).get(0);
                if (download && (contentType.contains("text") || contentType.contains("json"))) { // 如果是下载文件, 但响应的是错误码和错误描述
                    download = false;
                }

                if (download) { // 文件下载
                    File file = ResUtil.getResAsFile(is);
                    data.setFile(file);
                    data.setBody(file.getAbsolutePath());
                    result.setData(data);
                } else { // 文本数据
                    String charset = ResUtil.getResponseCharset(conn.getContentType());
                    String resBody = ResUtil.getResAsString(is, charset);
                    data.setBody(resBody);
                    result.setData(data);
                }
            } else {
                logger.debug("处理错误响应...");
                is = conn.getErrorStream();

                String charset = ResUtil.getResponseCharset(conn.getContentType());
                String resBody = ResUtil.getResAsString(is, charset);
                data.setBody(resBody);
                result.setData(data);
            }
            IOUtils.closeQuietly(is);

            logger.debug("响应数据: {}", JSON.toJSONString(data));
        } catch (Exception e) {
            result.error("Http请求发生异常: " + e.getMessage());
            logger.error("Http请求发生异常: " + this.url, e);
        } finally {
            IOUtils.close(conn);
        }

        logger.debug("响应结果:{} {}", result.getCode(), result.getMsg());
        return result;
    }

// ---------------------======= 初始化连接 ========---------------------------

    private void initConnection(HttpURLConnection conn) throws ProtocolException {
        {
            // 设置请求方式 GET
            conn.setRequestMethod(this.method.getValue());
        }
        { // 设置超时
            conn.setConnectTimeout(this.connectTimeout); // 设置连接主机超时(单位:毫秒)
            conn.setReadTimeout(this.readTimeout); // 设置从主机读取数据超时(单位:毫秒)
        }

        { // 设置常规属性
            /*
             * 设置请求头或响应头:HTTP请求允许一个key带多个用逗号分开的values,但是HttpURLConnection只提供了单个操作的方法:
             * setRequestProperty(key,value):会覆盖已经存在的key的所有values,有清零重新赋值的作用
             * addRequestProperty(key,value):是在原来key的基础上继续添加其他value
             */
            conn.setRequestProperty(Header.CONTENT_TYPE.getValue(), "application/x-www-form-urlencoded; charset=" + charset.name());
            conn.setRequestProperty(Header.ACCEPT.getValue(), "*/*"); // 设置接受的文件类型,*表示一切可以接受的
            conn.setRequestProperty(Header.ACCEPT_CHARSET.getValue(), charset.name()); // 新添加的请求头编码
//            conn.setRequestProperty(Header.ACCEPT_ENCODING.getValue(), "gzip, deflate"); // 导致 get 请求,响应中文乱码
            conn.setRequestProperty(Header.CONNECTION.getValue(), "Keep-Alive"); // 设置维持长连接
            conn.setRequestProperty(Header.USER_AGENT.getValue(), "WanMo-HttpClient/4.4 (Java 1.5 minimum; Java/1.8.0_321)");

            // 设定传送的内容类型是可序列化的java对象
            // (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)
            // conn.setRequestProperty("Content-type", "application/x-java-serialized-object");
        }

        if (MapUtil.isNotEmpty(this.header)) { // 属性覆盖或新增
            for (Map.Entry entry : this.header.entrySet()) {
                conn.setRequestProperty(entry.getKey(), entry.getValue());
            }
        }
    }

    private void initGet(HttpURLConnection conn) {
        // 设置是否使用缓存 默认是true
        conn.setUseCaches(true);
    }


    private void initPost(HttpURLConnection conn) throws Exception {
        // 设置是否从 httpUrlConnection 允许读入,默认情况下是 true;
        conn.setDoInput(true);
        // 默认情况下是 false; 设置是否向 httpUrlConnection 允许输出,因为这个是 post 请求,参数要放在 HTTP 正文内,因此需要设为 true,
        conn.setDoOutput(true);
        // Post 请求不能使用缓存
        conn.setUseCaches(false);

        if (isMultipart == false) {
            if (MapUtil.isNotEmpty(this.params)) {
                {
                    byte[] paramsBytes = toParamStr(this.params).getBytes(this.charset);

                    OutputStream out = conn.getOutputStream();  // 获取输出流
                    {
                        out.write(paramsBytes);
//                        out.write(CRLF.getBytes());
                        out.flush();
                    }
                    {
                        IOUtils.closeQuietly(out);
                    }
                }
            }

            if (StringUtil.isNotBlank(this.body)) { // 如果请求体不为空
                byte[] paramsBytes = this.body.getBytes(this.charset);

                OutputStream out = conn.getOutputStream();  // 获取输出流
                { // 当有数据需要提交时,请求参数传给服务器
                    out.write(paramsBytes);
//                    out.write(CRLF.getBytes());
                    out.flush();
                }
                {
                    IOUtils.closeQuietly(out);
                }
            }

        }

        if (isMultipart == true) {

            { // 请求开始
                conn.setRequestProperty(Header.CONTENT_TYPE.getValue(), "multipart/form-data; charset=" + charset.name() + "; boundary=" + BOUNDARY); // 定义数据分隔线
                conn.setRequestProperty(Header.CONTENT_LENGTH.getValue(), String.valueOf(BOUNDARY.length())); // 设置 boundary 的长度
            }

//            OutputStream out = conn.getOutputStream();  // 获取输出流
            OutputStream out = new DataOutputStream(conn.getOutputStream());
            { // 表单参数
                {
                    if (MapUtil.isEmpty(params)) {
                        params = new HashMap<>();
                    }
                    params.putIfAbsent("description", "文件上传");
                }

                for (Map.Entry entry : this.params.entrySet()) {
                    StringBuilder paramData = new StringBuilder();
                    paramData.append(TWO_HYPHEN).append(BOUNDARY).append(CRLF);
                    paramData.append("Content-Disposition: form-data; name=" + entry.getKey()).append(CRLF);
                    paramData.append("Content-Type: text/plain; charset=" + charset.name()).append(CRLF);
                    paramData.append("Content-Transfer-Encoding: 8bit").append(CRLF);
                    paramData.append(CRLF); // 参数头设置完以后需要两个换行,然后才是参数内容
                    paramData.append(entry.getValue());
                    paramData.append(CRLF);
                    String paramDataStr = paramData.toString();
                    out.write(paramDataStr.getBytes());
                    out.flush();
                    logger.debug("isMultipart paramData:\r\n {}", paramDataStr);
                }
            }

            for (Uploader uploader : this.uploaders.values()) { // 文件参数
                String name = uploader.getName();
                List files = uploader.getFiles();

                for (UploadFile uploadFile : files) {
                    { // 开始构建 multipart/form-data 格式的数据包
                        StringBuilder mediaData = new StringBuilder();
                        mediaData.append(TWO_HYPHEN).append(BOUNDARY).append(CRLF);
                        mediaData.append("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + uploadFile.getFilename() + "\"; filelength=" + uploadFile.getFile().length() + "; digest=" + uploadFile.getDigest() + CRLF);
                        String contentType = URLConnection.guessContentTypeFromName(uploadFile.getFile().getName());
                        mediaData.append("Content-Type:" + (StringUtil.isBlank(contentType) ? "application/octet-stream" : contentType) + "; charset=" + charset.name() + CRLF);
                        mediaData.append("Content-Transfer-Encoding: binary" + CRLF);
                        mediaData.append(CRLF); // 参数头设置完以后需要两个换行,然后才是参数内容
                        String mediaDataStr = mediaData.toString();
                        out.write(mediaDataStr.getBytes());
                        out.flush();
                        logger.debug("isMultipart mediaData: \r\n {}", mediaDataStr);
                    }
                    { // 文件上传
                        DataInputStream fs = new DataInputStream(new FileInputStream(uploadFile.getFile()));
                        int bytes = 0;
                        byte[] bufferOut = new byte[1024];
                        while ((bytes = fs.read(bufferOut)) != -1) {
                            out.write(bufferOut, 0, bytes);
                        }
                        out.flush();
                        IOUtils.closeQuietly(fs);
                    }
                    { // 换行
                        byte[] end_data = (CRLF).getBytes();
                        out.write(end_data);
                        out.flush();
                    }
                }
            }

            { // 请求截止
                byte[] end_data = (TWO_HYPHEN + BOUNDARY + TWO_HYPHEN + CRLF).getBytes();
                out.write(end_data);
                out.flush();
            }

            {
                IOUtils.closeQuietly(out);
            }
        }
    }


    // ---------------------======= 根据 http https 获取连接 ========---------------------------
    private HttpURLConnection getConnection() throws Exception {
        logger.debug("请求URL: {}", this.url);
        logger.debug("请求方式: {}", this.method);
        logger.debug("请求头: {}", JSON.toJSONString(this.header));
        logger.debug("请求数据: {}", this.body);
        logger.debug("请求文件: {}", JSON.toJSONString(this.uploaders.values()));

        URL url = new URL(this.url);

        HttpURLConnection conn = null;
        if ("HTTPS".equals(url.getProtocol().toUpperCase())) { // 如果是安全连接则需要
            conn = getConnectionHttps(url);
        } else {
            conn = getConnectionHttp(url);
        }

        initConnection(conn);

        switch (method) { // 根据请求方式,初始化 conn
            case GET:
                initGet(conn);
                break;
            case POST:
                initPost(conn);
                break;
            default:
                throw new RuntimeException("不支持的请求方式:" + method);
        }

        conn.connect();
        return conn;
    }

    /**
     * 获取 http 连接
     *
     * @param url
     * @return
     * @throws Exception
     */
    private HttpURLConnection getConnectionHttp(URL url) throws Exception {
        return (HttpURLConnection) url.openConnection();
    }

    /**
     * 获取 https 连接
     *
     * @param url
     * @return HttpsURLConnection
     * @throws Exception
     */
    private HttpsURLConnection getConnectionHttps(URL url) throws Exception {
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        conn.setHostnameVerifier(new NullHostNameVerifier());
//			connectionHttps.setSSLSocketFactory(getSSLSocket());
        conn.setSSLSocketFactory(SSLContextFactory.createSSLContext(this.certFilePath, this.certFilePassword).getSocketFactory());
        return conn;
    }


// ---------------------======= 工具方法 ========---------------------------

    /**
     * 将map转为 参数字符串:name1=value1&name2=value2 的形式
     *
     * @param param
     * @return 字符串
     * @throws UnsupportedEncodingException
     */
    public static String toParamStr(Map param) {
        String reStr = "";

        if (MapUtil.isEmpty(param)) {
            return reStr;
        }

        Set> entrySet = param.entrySet();
        for (Map.Entry o : entrySet) {
            if (o.getValue() == null || "null".equals(o.getValue()) || "class".equals(o.getKey())) {
                continue;
            }
            String s = o.getKey() + "=" + o.getValue();
            reStr += (s + "&");
        }

        return StringUtil.isBlank(reStr) ? "" : reStr.substring(0, reStr.length() - 1);
    }

    /**
     * 将字符串 (name1=value1&name2=value2)的形式,转为 Map 形式
     *
     * @param param
     * @return Map
     * @throws UnsupportedEncodingException
     */
    public static Map toParamMap(String param) {
        Map paramMap = new HashMap<>();
        if (StringUtil.isBlank(param)) {
            return paramMap;
        }

        String[] paramPairsArr = param.split("&");
        for (String paramPair : paramPairsArr) {
            String[] paramPairArr = paramPair.split("=");
            if (paramPairArr.length == 2) {
                paramMap.put(paramPairArr[0].trim(), paramPairArr[1].trim());
            } else {
                paramMap.put(paramPairArr[0].trim(), "");
            }

        }

        return paramMap;
    }

    /**
     * 判断是否是 HTTP URL
     *
     * @param url 请求 url
     * @return 布尔值
     */
    public static Boolean isHttpUrl(String url) {
        boolean flag = false;

        try {
            flag = url.indexOf("://") != -1;
        } catch (Exception e) {
        }

        return flag;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy