HttpURLConnection实现参数+文件传输
Posted zdyang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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。依然乱码。又参照这条博文解决 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上了,有时间可以去看看。
以上是关于HttpURLConnection实现参数+文件传输的主要内容,如果未能解决你的问题,请参考以下文章
java Web工程,如何在不传参数的情况下,判断WebService接口是不是开放