使用原生JDK写一个HttpClient发送POST上传图片和表单数据
使用HttpUrlConnection
一个支持HTTP特定功能的URLConnection。
使用这个类遵循以下模式:
1.通过调用URL.openConnection()来获得一个新的HttpURLConnection对象,并且将其结果强制转换为HttpURLConnection.
2.准备请求。一个请求主要的参数是它的URI。请求头可能也包含元数据,例如证书,首选数据类型和会话cookies.
3.可以选择性的上传一个请求体。HttpURLConnection实例必须设置setDoOutput(true),如果它包含一个请求体。通过将数据写入一个由getOutStream()返回的输出流来传输数据。
4.读取响应。响应头通常包含元数据例如响应体的内容类型和长度,修改日期和会话cookies。响应体可以被由getInputStream返回的输入流读取。如果响应没有响应体,则该方法会返回一个空的流。
5.关闭连接。一旦一个响应体已经被阅读后,HttpURLConnection 对象应该通过调用disconnect()关闭。断开连接会释放被一个connection占有的资源,这样它们就能被关闭或再次使用。
主要的流程
创建URL实例,打开URLConnection
URL url=new URL("http://www.baidu.com");
HttpURLConnection connection= (HttpURLConnection) url.openConnection();
设置连接参数
常用方法:
setDoInput
setDoOutput
setIfModifiedSince:设置缓存页面的最后修改时间(参考自:http://blog.csdn.net/stanleyqiu/article/details/7717235)
setUseCaches
setDefaultUseCaches
setAllowUserInteraction
setDefaultAllowUserInteraction
setRequestMethod:HttpURLConnection默认给使用Get方法
设置请求头参数
常用方法:
setRequestProperty(key,value)
addRequestProperty(key,value)
setRequestProperty和addRequestProperty的区别就是,setRequestProperty会覆盖已经存在的key的所有values,有清零重新赋值的作用。而addRequestProperty则是在原来key的基础上继续添加其他value。
常用设置:
设置请求数据类型:
connection.setRequestProperty("Content-type","application/x-javascript->json");//json格式数据
connection.addRequestProperty("Content-Type","application/x-www-form-urlencoded");//默认浏览器编码类型,http://www.cnblogs.com/taoys/archive/2010/12/30/1922186.html
connection.addRequestProperty("Content-Type","multipart/form-data;boundary="+boundary);//post请求,上传数据时的编码类型,并且指定了分隔符
Connection.setRequestProperty("Content-type", "application/x-java-serialized-object");// 设定传送的内容类型是可序列化的java对象(如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)
connection.addRequestProperty("Connection","Keep-Alive");//设置与服务器保持连接
connection.addRequestProperty("Charset","UTF-8");//设置字符编码类型
连接并发送请求
connect
getOutputStream
在这里getOutStream会隐含的进行connect,所以也可以不调用connect
获取响应数据
getContent (https://my.oschina.net/zhanghc/blog/134591)
getHeaderField:获取所有响应头字段
getInputStream
getErrorStream:若HTTP响应表明发送了错误,getInputStream将抛出IOException。调用getErrorStream读取错误响应。
post上传文件和表单数据:
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Map;
/**
* post文件上传 JDK
*/
public class UploadFile {
/**
* 上传文件到指定Url
* @param url 请求地址
* @param file 上传文件
* @param params 其他参数集合
* @param header 请求头
* @return
*/
public static String uploadFile(String url, File file, Map<String, String> params, Map<String,String> header) {
String message = "";
String boundary = "******"; //随机分割线
InputStream inputStream = null;
FileInputStream fileInputStream = null;
OutputStream outputStream = null;
HttpURLConnection connection = null;
try {
URL url1 = new URL(url);
connection = (HttpURLConnection) url1.openConnection();
connection.setRequestMethod("POST");
connection.addRequestProperty("Connection", "Keep-Alive");
connection.addRequestProperty("Charset", "UTF-8");
connection.addRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
//添加请求头
for (Map.Entry<String, String> entry:header.entrySet()){
connection.addRequestProperty(entry.getKey(),entry.getValue());
}
// 设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在
// http正文内,因此需要设为true, 默认情况下是false;
connection.setDoOutput(true);
//设置是否从httpUrlConnection读入,默认情况下是true;
connection.setDoInput(true);
// Post 请求不能使用缓存 ?
connection.setUseCaches(false);
connection.setConnectTimeout(20000);
outputStream = connection.getOutputStream();
StringBuilder sb = new StringBuilder();
if (file != null) {
//文件上传
fileInputStream = new FileInputStream(file);
sb.append("--" + boundary + "\r\n")
.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + URLEncoder.encode(file.getName(), "UTF-8") + "\"\r\n")
.append("Content-Type: application/pdf"+"\r\n")
.append("\r\n");
/*outputStream.write(("--" + boundary + "\r\n").getBytes("utf-8"));
// 设定传送的内容类型是可序列化的java对象
// (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)
outputStream.write(("Content-Disposition: form-data; name=\"file\"; filename=\"" + URLEncoder.encode(file.getName(), "UTF-8") + "\"\r\n").getBytes("utf-8"));
outputStream.write(("Content-Type: application/pdf"+"\r\n").getBytes("utf-8"));*/
outputStream.write(sb.toString().getBytes("utf-8"));
byte[] b = new byte[1024];
while ((fileInputStream.read(b)) != -1) {
outputStream.write(b);
}
outputStream.write(("\r\n").getBytes("utf-8"));
}
//数据上传
for (Map.Entry<String, String> entry:params.entrySet()){
sb.append("\r\n")
.append("--" + boundary + "\r\n")
.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n")
.append("\r\n")
.append(entry.getValue() + "\r\n");
}
sb.append("--" + boundary +"--"+ "\r\n");
outputStream.write(sb.toString().getBytes("utf-8"));
if (connection.getResponseCode() >= 300) {
throw new Exception("HTTP Request is not success, Response code is " + connection.getResponseCode());
}
if (connection.getResponseCode() == 200) {
inputStream = connection.getInputStream();
Reader reader = new InputStreamReader(inputStream, "utf-8");
StringBuilder response = new StringBuilder();
final char[] buff = new char[1024];
int read = 0;
while ((read = reader.read(buff)) > 0) {
response.append(buff, 0, read);
}
return response.toString();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
connection.disconnect();
}
return message;
}
这里需要指出:
通过chrome的开发工具截取的头信息可以看到:
通过post上传数据时,若除了文本数据以外还要需要上传文件,则需要在指定每一条数据的Content-Disposition,name,若是文件还要指明filename,并在每条数据传输的后面用“–”加上boundary隔开,并且需要在第四行用“\r\n”换行符隔开,在最后一行也要用“–”加上boundary加上“–”隔开,否则会导致文件上传失败!