slider

还是菜鸟
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

网络编程之HttpURLConnection

Posted on 2012-06-07 16:14  slider  阅读(1466)  评论(1编辑  收藏  举报

  最近忙于android端网络,看了sina微博api,发现它使用的是HttpClient.记得以前使用开发renren网应用的时候,它sdk包里面的使用的是HttpURLConnection. 我把他们两个分别调试了下,都是可行的。学习之余,就想知道他们两个的区别及优缺点。

  个人感觉HttpURLConnection青量,稍底层一些(当年我写j2me的时候就是用的它吧)。而httpClient是apache的东东,似乎更高级一些。当然这些都是个人的感觉。我也曾经纠结于到底使用httpclient还是httpurlConnection。最近看到有人说post on the Google Developers blog上推荐httpurlconnection.虽然我找不到原文,但是我找到一个截图:

For Gingerbread and better, HttpURLConnection is the best choice. Its simple API and small size makes it great fit for Android. Transparent compression and response caching reduce network 
use, improve speed and save battery. New applications should use HttpURLConnection; it is where we will be spending our energy going forward.

   那我以后就不用在纠结了。下面就说说一些httpurlconnection的用法吧。我学习喜欢把东西写在代码注释里面,这样方便的我下次直接使用和学习。

  

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HttpUtil {
	private static final String BOUNDARY = "---------7d4a6d158c9";	//分割符号
	
	private static HttpUtil instance = null;
	
	private HttpUtil(){	}
	
	public synchronized static HttpUtil getInstance(){
		if(instance==null){
			instance = new HttpUtil();
		}
		return instance;
	}
	
	public String connectGet(String url,HashMap<String, String> params){
		String response = "";
		URL urlGet;
		HttpURLConnection connection;
		try{
			if(params!=null)
				url = url +"?"+Util.encodeParams(params);
			urlGet = new URL(url);
			connection = (HttpURLConnection) urlGet.openConnection();
			
			InputStream is = null;
			int responseCode = connection.getResponseCode();
			if (responseCode == 200){
                is = connection.getInputStream();
            }else{
            	is = connection.getErrorStream();
            }
			response = read(is);
			
		}catch(IOException e){
			e.printStackTrace();
		}
		return response;

	}
	public String connectPost(String url,HashMap<String, String> params){
		String response = "";
		URL urlPost;
		HttpURLConnection connection;
		try{
			urlPost = new URL(url);
			connection = (HttpURLConnection) urlPost.openConnection();
			
			// 设定请求的方法为"POST",默认是GET   
			connection.setRequestMethod("POST");  
			connection.setDoOutput(true);  //设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在http正文内,因此需要设为true, 默认情况下是false;   
			//connection.setDoInput(true);  // 设置是否从httpUrlConnection读入,默认情况下是true;   ---- 這個实际上沒有必要设置
			connection.setUseCaches(false); //Post 请求不能使用缓存,因为要保证post数据安全
			connection.setConnectTimeout(5000);// (单位:毫秒)jdk
			connection.setReadTimeout(5000);// (单位:毫秒)jdk 1.5换成这个,读操作超时
			connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			connection.connect(); 
			
		    // 此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法,   
		    // 所以在开发中不调用上述的connect()也可以)。   
		    OutputStream os = connection.getOutputStream();  
			os.write(Util.encodeParams(params).getBytes()); // 这里数据的组织形式在你
		    os.flush();
		    os.close();
		    
		    //这里为止,所有的http请求设置,包括数据都应经完毕,下面一布进行从httpUrlConnection读数据
		    //HttpURLConnection的connect()函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。
		    //无论是post还是get,http请求实际上直到HttpURLConnection的getInputStream()这个函数里面才正式发送出去。
		    InputStream is = connection.getInputStream();		
			response = read(is);
			
		}catch(IOException e){
			e.printStackTrace();
		}
		return response;
	}
	
	public String openUrl(String url,HashMap<String, String> params,String method,List<FileEntity> file){
		String response = "";
		if(method.equals("GET")){
			response = connectGet(url,params);
		}else if(method.equals("POST")){
			if(file==null){
				response = connectPost(url, params);
			}else{
				response = connectPost(url, params, file);
			}
		}
		return response;
	}
	
	/**
	 * post 上传文件(包括多个文件)
	 * @param url
	 * @param params 传递的是参数
	 * @param fileparams 文件数据
	 * @param contentType 文件类型
	 * @return
	 */
	public  String connectPost(String url,HashMap<String, String> params,List<FileEntity> fileparams){
		String response = "";
		URL urlPost;
		HttpURLConnection connection;
		try{
			urlPost = new URL(url);
			connection = (HttpURLConnection) urlPost.openConnection();
			
			// 设定请求的方法为"POST",默认是GET   
			connection.setRequestMethod("POST");  
			connection.setDoOutput(true);  //设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在http正文内,因此需要设为true, 默认情况下是false;   
			connection.setDoInput(true);  // 设置是否从httpUrlConnection读入,默认情况下是true;   ---- 這個实际上沒有必要设置
			connection.setUseCaches(false); //Post 请求不能使用缓存,因为要保证post数据安全
			connection.setConnectTimeout(5000);// (单位:毫秒)jdk
			connection.setReadTimeout(5000);// (单位:毫秒)jdk 1.5换成这个,读操作超时
			connection.setRequestProperty("connection", "keep-alive");
			connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
			connection.connect(); 
			
		    // 此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法,   
		    // 所以在开发中不调用上述的connect()也可以)。 
		    OutputStream os = connection.getOutputStream();  
		    writeFilesParams(os, fileparams);
		    os.flush();
		    os.close();
		    
		    //这里为止,所有的http请求设置,包括数据都应经完毕,下面一布进行从httpUrlConnection读数据
		    //HttpURLConnection的connect()函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。
		    //无论是post还是get,http请求实际上直到HttpURLConnection的getInputStream()这个函数里面才正式发送出去。
		    InputStream is = connection.getInputStream();		
			response = read(is);
			
		}catch(IOException e){
			e.printStackTrace();
		}
		return response;
	}
	/**
	 * 读取从服务器返回的数据
	 * @param in
	 * @return
	 * @throws IOException
	 */
	private static String read(InputStream in) throws IOException {
        StringBuilder sb = new StringBuilder();
        BufferedReader r = new BufferedReader(new InputStreamReader(in), 1024);
        for (String line = r.readLine(); line != null; line = r.readLine()) {
            sb.append(line);
        }
        in.close();
        return sb.toString();
    }
	
	
	/**
	 * 把文件写书outputStream流里面
	 * @param os
	 * @param fileparams 文件数据
	 * @param contentType 文件类型
	 */
	private void writeFilesParams(OutputStream os,List<FileEntity> fileparams){
		for (FileEntity fileEntity:fileparams) {  
            String name = fileEntity.getName();  
            File value = fileEntity.getValue(); 
            StringBuilder sb = new StringBuilder();
            String dataStart = sb.append("--" + BOUNDARY + "\r\n")
            		.append("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" + value.getName() + "\"\r\n")
            		.append("Content-Type: " + parseContentType(value) + "\r\n\r\n").toString();
            String dataEnd = "\r\n--" + BOUNDARY + "--\r\n";

			try {
				os.write(dataStart.getBytes());
				FileInputStream fis;
				fis = new FileInputStream(fileEntity.getValue());
				int rn2;  
	            byte[] buf = new byte[1024];  
	            while((rn2=fis.read(buf, 0, 1024))>0){     
	                os.write(buf,0,rn2);   
	            }
	            os.write(dataEnd.getBytes());
			} catch (IOException e) {
				e.printStackTrace();
			}    
        }  
	}
	
	private String parseContentType(File file) {
		String contentType = "image/jpg";
		String fileName = file.getName();
		fileName = fileName.toLowerCase();
		if (fileName.endsWith(".jpg"))
			contentType = "image/jpg";
		else if (fileName.endsWith(".png"))
			contentType = "image/png";
		else if (fileName.endsWith(".jpeg"))
			contentType = "image/jpeg";
		else if (fileName.endsWith(".gif"))
			contentType = "image/gif";
		else if (fileName.endsWith(".bmp"))
			contentType = "image/bmp";
		else
			throw new RuntimeException("不支持的文件类型'" + fileName + "'(或没有文件扩展名)");
		return contentType;
	}
	/**
	 * 获取服务器返回的http头部信息,这里只是输出   :  这个功能用的少如服务器返回cookie就在http响应头部
	 * @param connection
	 */
	private void getResponseHeader(HttpURLConnection connection){
		
		Map<String, List<String>> headers  = connection.getHeaderFields();
		for(String key:headers.keySet()){
			System.out.print(key+" : ");
			List<String> data = headers.get(key);
			for(String value:data){
				System.out.print(value);
			}
			System.out.println();
		}
	}
	
	public static void main(String[] args) {
		String url = "http://www.baidu.com";
		//System.out.println(HttpUtil.getInstance().connectGet(url));
		new AsyncRunner().run(url, null, "GET", null,null);
		System.out.println();
	}
}

 

  下面就说说httpurlConnection的一些注意事项吧(这个是网上摘抄的):

  a:) HttpURLConnectionconnect()函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。无论是post还是gethttp请求实际上直到HttpURLConnectiongetInputStream()这个函数里面才正式发送出去。
       b:) 在用POST方式发送URL请求时,URL请求参数的设定顺序是重中之重, connection对象的一切配置(那一堆set函数)都必须要在connect()函数执行之前完成。而对outputStream的写操作,又必须要在inputStream的读操作之前。这些顺序实际上是由http请求的格式决定的。如果inputStream读操作在outputStream的写操作之前,会抛出例外: java.net.ProtocolException: Cannot write output after reading input.......
   c:) http请求实际上由两部分组成, 一个是http头,所有关于此次http请求的配置都在http头里面定义, 一个是正文contentconnect()函数会根据HttpURLConnection对象的配置值生成http头部信息,因此在调用connect函数之前, 就必须把所有的配置准备好。
       d:) http头后面紧跟着的是http请求的正文,正文的内容是通过outputStream流写入的,  实际上outputStream不是一个网络流,充其量是个字符串流,往里面写入的东西不会立即发送到网络,而是存在于内存缓冲区中,待outputStream流关闭时,根据输入的内容生成http正文。至此,http请求的东西已经全部准备就绪。在getInputStream()函数调用的时候,就会把准备好的http请求 正式发送到服务器了,然后返回一个输入流,用于读取服务器对于此次http请求的返回信息。由于http 请求在getInputStream的时候已经发送出去了(包括http头和正文),因此在getInputStream()函数 之后对connection对象进行设置(对http头的信息进行修改)或者写入outputStream(对正文进行修改) 都是没有意义的了,执行这些操作会导致异常的发生。