HttpURLConnection实现参数+文件传输

首先看一下别人写的非常全的:

https://www.cnblogs.com/tenWood/p/8563617.html

以及我用了后依然有乱码,解决参考:

https://www.cnblogs.com/cornucopia/p/4498177.html

 

历程如下::::

 

做商汤人脸识别系统,对方提供的示例为:

package com.sensetime.bi.senselink.open.api;

import javax.net.ssl.SSLException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class ApiDemo {

    //输入你的app_key
    private final static String app_key = "586949450e231c71";
    //输入你的app_secret
    private final static String app_secret = "e27c8593b933fe892969877941d729dd";
    //本机要上传的图片位置
    private final static String path = "F://123.png";
    //要使用的api的uri
    private final static String uri = "/api/v1/recognition/check";

    private final static String BOUNDARY = "----WebKitFormBoundarygrBcuHVTeNQcBtqn";
    private static String name;

    public static void main(String[] args) throws Exception {
        String url = "https://link.bi.sensetime.com" + uri;
        //表单文本参数
        Map<String, Object> params = new HashMap();
        //表单文件参数
        Map<String, byte[]> fileParams = new HashMap();

        File avatar_file = new File(path);
        name = avatar_file.getName();
        Long timestamp = getTimestamp();
        //添加表单文本参数
        {
            params.put("app_key", app_key);
            params.put("timestamp", String.valueOf(timestamp));
            params.put("sign", getSign(String.valueOf(timestamp)));
        }
        //添加表单文件参数
        {
            fileParams.put("face_avatar", getBytes(avatar_file));
        }
        String result = new String(doPost(url, params, fileParams));
        System.out.println(result);

    }

    public static byte[] doPost(String strUrl, Map<String, Object> params, Map<String, byte[]> fileParams) throws Exception {
        URL url = new URL(strUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setInstanceFollowRedirects(true);
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Accept", "application/json, text/plain, */*"); // 设置接收数据的格式
        connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); // 设置发送数据的格式
        connection.connect();
        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
        Iterator it = params.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry) it.next();
            String key = entry.getKey();
            String value = entry.getValue();
            out.writeBytes("--" + BOUNDARY + "\r\n");
            out.writeBytes("Content-Disposition: form-data; name=\"" + key + "\"");
            out.writeBytes("\r\n\r\n");
            out.writeBytes(value + "\r\n");
        }
        if (fileParams != null && fileParams.size() > 0) {
            Iterator fileIt = fileParams.entrySet().iterator();
            while (fileIt.hasNext()) {
                Map.Entry<String, byte[]> fileEntry = (Map.Entry<String, byte[]>) fileIt.next();
                out.writeBytes("--" + BOUNDARY + "\r\n");
                out.writeBytes("Content-Disposition: form-data; name=\"" + fileEntry.getKey()
                        + "\"; filename=\"" + name + "\"");
                out.writeBytes("\r\n");
                out.writeBytes("Content-Type: image/jpeg");//此处很关键
                out.writeBytes("\r\n\r\n");
                out.write(fileEntry.getValue());
                out.writeBytes("\r\n");
            }
        }
        out.writeBytes("--" + BOUNDARY + "--");
        out.flush();
        out.close();
        InputStream in = null;
        int code = connection.getResponseCode();
        try {
            if (code == 200) {
                in = connection.getInputStream();
            } else {
                in = connection.getErrorStream();
            }
        } catch (SSLException e) {
            e.printStackTrace();
            return new byte[0];
        }
        ByteArrayOutputStream baout = new ByteArrayOutputStream();
        byte[] buff = new byte[1024];
        int len;
        while ((len = in.read(buff)) != -1) {
            baout.write(buff, 0, len);
        }
        byte[] bytes = baout.toByteArray();
        in.close();
        return bytes;
    }


    public static Long getTimestamp() {
        return System.currentTimeMillis();
    }
    
    public static String getSign(String timestamp) {
        String code = timestamp + "#" + app_secret;
        return getMD5(code);
    }
    
    private static String getMD5(String sourceStr) {
        String result = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(sourceStr.getBytes());
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }
            result = buf.toString();
        } catch (NoSuchAlgorithmException e) {
            System.out.println(e);
        }
        return result;
    }
    
    public static byte[] getBytes(File f) {
        try {
            InputStream in = new FileInputStream(f);
            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
            byte[] b = new byte[1024];
            int n;
            while ((n = in.read(b)) != -1)
                out.write(b, 0, n);
            in.close();
            out.close();
            return out.toByteArray();
        } catch (IOException e) {
            System.out.println("***请设置文件路径***");
        }
        return null;
    }
}

用起来ok,但中文显示乱码。后来上网找方法,参照这条博文https://www.cnblogs.com/tenWood/p/8563617.html。将out.writeBytes(content)替换为out.write(content.getBytes())  。依然乱码。

又参照这条博文解决 https://www.cnblogs.com/cornucopia/p/4498177.html。心里历程如下:

/**
运行后结果对于中文传递过去后是乱码。
            原因:
            out.writeBytes();这个方法点进去结构为:
            public final void writeBytes(String s) throws IOException {
                int len = s.length();
                for (int i = 0 ; i < len ; i++) {
                out.write((byte)s.charAt(i));
            }
                incCount(len);
            }
            因为java里的char类型是16位的,一个char可以存储一个中文字符,在将其转换为 byte后高8位会丢失,这样就无法将中文字符完整的输出到输出流中。
            所以在可能有中文字符输出的地方最好先将其转换为字节数组,然后再通过write写入流,
            目前尝试过这种方法:把上面链接代码中的out.writeBytes(content);替换为out.write(content.getBytes());先把数据转成BYTE在写入流,执行成功.
            执行成功,但还是乱码,找了很久,后来把out.write(content.getBytes())改为out.write(content.getBytes("utf-8"))解决。
            原因是:
                   在Java中,String的getBytes()方法是得到一个操作系统默认的编码格式的字节数组。这表示在不同的操作系统下,返回的东西不一样!
                          把String转换成bytes,各种编码转换成的bytes不同,比如UTF-8每个汉字转成3bytes,而GBK转成2bytes,所以要说明编码方式,否则用缺省编码。
                   (另外:与getBytes相对的,可以通过new String(byte[], decode)的方式来还原)
**/

 

最终代码:

public static String doPost(String strUrl, Map<String, Object> params, Map<String, byte[]> fileParams,String fileName) throws Exception {
        logger.info("*******doPost1()参数为:\n strUrl:"+strUrl +"\n params:"+params +" \n fileParams:"+fileParams+" \n fileName:"+fileName);
        String ret = null;
        URL url = null;
        InputStream in = null;
        String TWO_HYPHENS = "--";
        String LINE_END = "\r\n";
        try {
            url = new URL(strUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();//得到connection对象
            /************************************设置请求头*************************************************/
            connection.setRequestMethod("POST");    //设置请求方式为POST
            connection.setDoOutput(true);           //允许写出
            connection.setDoInput(true);            //允许读入
            connection.setUseCaches(false);         //不使用缓存
            connection.setInstanceFollowRedirects(true);//本次连接是否自动处理重定向(true:系统自动处理重定向;false:则需要自己从http reply中分析新的url)(置所有的http连接是否自动处理重定向:public static void HttpURLConnection.setFollowRedirects(boolean followRedirects))
            connection.setRequestProperty("Charset", "utf-8");//编码格式
            connection.setRequestProperty("Content-Type", "multipart/form-data ; boundary=" + BOUNDARY); // 设置发送数据的格式(form-data格式)   //boundary为头部分隔符,头部拼接时需要分隔符。例如下面的有多个"Content-Disposition"拼接时需要用到此分隔符
            connection.setRequestProperty("Accept", "application/json, text/plain, */*");               // 设置接收数据的格式(json格式)
            connection.connect(); //连接
            /************************************输出流,写数据,start*************************************************/
            DataOutputStream out = new DataOutputStream(connection.getOutputStream());//获得输出流对象
            StringBuffer strBufparam = new StringBuffer();
            Iterator it = params.entrySet().iterator();
            while (it.hasNext()) {
                //封装键值对数据
                Map.Entry<String, String> entry = (Map.Entry) it.next();
                String key = entry.getKey();
                String value = entry.getValue();

                strBufparam.append(TWO_HYPHENS);
                strBufparam.append(BOUNDARY);
                strBufparam.append(LINE_END);//"--" + BOUNDARY + "\r\n"
                strBufparam.append("Content-Disposition: form-data; name=\"" + key + "\"");
                strBufparam.append(LINE_END);
                strBufparam.append(LINE_END);
                strBufparam.append(value);
                strBufparam.append(LINE_END);
            }
            out.write(strBufparam.toString().getBytes("utf-8"));
            strBufparam.toString().getBytes();
            //写入图片参数
            if (fileParams != null && fileParams.size() > 0) {
                Iterator fileIt = fileParams.entrySet().iterator();
                while (fileIt.hasNext()) {
                    Map.Entry<String, byte[]> fileEntry = (Map.Entry<String, byte []>) fileIt.next();
                    //拼接文件的参数
                    StringBuffer strBufFile = new StringBuffer();
                    strBufFile.append(TWO_HYPHENS);
                    strBufFile.append(BOUNDARY);
                    strBufFile.append(LINE_END);
                    //strBufFile.append("Content-Disposition: form-data; name=\"" + "image" + "\"; filename=\"" + file.getName() + "\"");
                    strBufFile.append("Content-Disposition: form-data; name=\"" + fileEntry.getKey() + "\"; filename=\"" + fileName + "\"");// fileEntry.getKey():文件全路径。fileName:文件名称
                    strBufFile.append(LINE_END);
                    strBufFile.append("Content-Type: image/jpeg");//此处很关键----文件格式
                    strBufFile.append(LINE_END);
                    strBufFile.append(LINE_END);
                    out.write(strBufFile.toString().getBytes());
                    out.write(fileEntry.getValue());//文件 (此参数之前调用了本页面的重写方法getBytes(File f),将文件转换为字节数组了 )
                    out.write((LINE_END).getBytes());
                }
            }

            //写入标记结束位
            byte[] endData = ( TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + LINE_END).getBytes();//写结束标记位
            out.write(endData);
            out.flush();
            out.close();
            /*
            下面是商汤提供的示例方法。
            DataOutputStream out = new DataOutputStream(connection.getOutputStream());//获得输出流对象
            Iterator it = params.entrySet().iterator();
            while (it.hasNext()) {
                //写入键值对数据一
                Map.Entry<String, String> entry = (Map.Entry) it.next();
                String key = entry.getKey();
                String value = entry.getValue();
                out.writeBytes("--" + BOUNDARY + "\r\n");
                out.writeBytes("Content-Disposition: form-data; name=\"" + key + "\"");
                out.writeBytes("\r\n\r\n");
                out.writeBytes(value + "\r\n");
            }
            //写入图片参数
            if (fileParams != null && fileParams.size() > 0) {
                Iterator fileIt = fileParams.entrySet().iterator();
                while (fileIt.hasNext()) {
                    Map.Entry<String, byte[]> fileEntry = (Map.Entry<String, byte[]>) fileIt.next();
                    out.writeBytes("--" + BOUNDARY + "\r\n");
                    out.writeBytes("Content-Disposition: form-data; name=\"" + fileEntry.getKey() + "\"; filename=\"" + fileName + "\"");// fileEntry.getKey():文件全路径。fileName:文件名称
                    out.writeBytes("\r\n");
                    out.writeBytes("Content-Type: image/jpeg");//此处很关键----文件格式
                    out.writeBytes("\r\n\r\n");
                    out.write(fileEntry.getValue());//文件
                    out.writeBytes("\r\n");
                }
            }
            out.writeBytes("--" + BOUNDARY + "--");
            out.flush();
            out.close();
             */


            /************************************输出流,写数据完成end*************************************************/
            int code = connection.getResponseCode(); //获得响应码(200为成功返回)
            try {
                if (code == HttpURLConnection.HTTP_OK) {
                    in = connection.getInputStream(); //获取响应流
                } else {
                    in = connection.getErrorStream(); //获取响应流
                }
            } catch (SSLException e) {
                e.printStackTrace();
                return "";
            }
            /**********读取返回的输入流信息**************/
            byte[] bytes = null;
            ByteArrayOutputStream baout = new ByteArrayOutputStream();
            byte[] buff = new byte[1024];
            int len;
            while ((len = in.read(buff)) != -1) {
                baout.write(buff, 0, len);
            }
            bytes = baout.toByteArray();
            in.close();
            ret = new String(bytes) ;
        }catch(Exception e){
            logger.error("向商汤服务器发送指令时出错doPost():"+e.getMessage());
            e.printStackTrace();
        }
        return ret;
    }

 /**
     * 将文件转换为byte数组
     * @param f
     * @return
     */
    public static byte[] getBytes(File f) {
        try {
            InputStream in = new FileInputStream(f);
            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
            byte[] b = new byte[1024];
            int n;
            while ((n = in.read(b)) != -1)
                out.write(b, 0, n);
            in.close();
            out.close();
            return out.toByteArray();
        } catch (IOException e) {
            logger.error("***请设置文件路径***");
            e.printStackTrace();
        }
        return null;
    }

 

 

在 https://www.cnblogs.com/tenWood/p/8563617.html博文下面,作者说封装了方法,放到github上了,有时间可以去看看。

 

上一篇:Java中的异步Web请求?


下一篇:java – 使用HttpURLConnection流式传输数据