Android实战之旅 006Android中的HTTP通信

须知少年凌云志,曾许人间第一流。这篇文章主要讲述Android实战之旅 006Android中的HTTP通信相关的知识,希望能为你提供帮助。
对于http协议我想很多朋友并不陌生, 一次HTTP操作称为一个事务。http中需要掌握的基础知识包括: URL,http三次握手, http请求头信息, http请求的方式, http响应码信息, http协议的特点, TCP/IP四层协议, OSI七层协议, 以及http1.0和http1.1区别。这里需要注意的http无连接: 限制每一次连接只处理一个请求, 服务器处理完客户的请求, 并收到客户的应答后, 即断开连接。http无状态: 协议对于事务处理没有记忆能力, 缺少状态意味着如果后续处理需要前面的信息, 则它必须重传。基础知识可以参考: http://blog.csdn.net/davebobo/article/details/52728841
开发环境:
android Studio1.5 RC 1
Myeclipse 10
jdk-7u80-windows-x64
apache-tomcat-7.0.69

一、HttpURLConnection的介绍及使用 案例一: http访问百度, WebView展示内容
新建http_01项目, 首先我们需要进行网络操作, 在AndroidManifest.xml中添加permission权限

< uses-permission android:name= " android.permission.INTERNET" />

activity_main.xml中添加WebView组件

< WebView android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:id= " @ + id/webView" android:layout_alignParentTop= " true" android:layout_alignParentLeft= " true" android:layout_alignParentStart= " true" android:layout_alignParentRight= " true" android:layout_alignParentEnd= " true" android:layout_alignParentBottom= " true" />

由于网络操作是一个耗时操作, 我们在线程中执行, 创建VisitWeb类继承Thread重写run方法处理网络耗时操作。

package com.davebobo.http_01; import android.media.tv.TvView; import android.os.Handler; import android.webkit.WebView; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * Created by DaveBobo on 2016/10/9. */ public class VisitWeb extends Thread{privateString url; private WebView webView; private Handler handler; public VisitWeb(String url, WebView webView, Handler handler){ this.url = url; this.webView = webView; this.handler = handler; }@ Override public void run() { super.run(); try { URL httpUrl = new URL(url); try { HttpURLConnection conn = (HttpURLConnection)httpUrl.openConnection(); conn.setReadTimeout(5000); //设置超时等待 conn.setRequestMethod(" GET" ); final StringBuffer sb = new StringBuffer(); //缓存 //通过网址回传网页流数据 BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); String str; while ((str = reader.readLine())!= null){ sb.append(str); }handler.post(new Runnable(){ @ Override public void run() { webView.loadData(sb.toString()," text/html; charset= utf-8" ,null); } }); } catch (IOException e) { e.printStackTrace(); } } catch (MalformedURLException e) { e.printStackTrace(); } } }

在MainActivity中查找WebView创建VisitWeb对象调用它的start()方法。

package com.davebobo.http_01; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.webkit.WebView; import android.widget.ImageView; public class MainActivity extends AppCompatActivity {private WebView webView; private Handler handler = new Handler(); private ImageView imageView; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView = (WebView) findViewById(R.id.webView); imageView = (ImageView) findViewById(R.id.imageView); new VisitWeb(" https://www.baidu.com/" ,webView,handler).start(); //new DownImage(" http://avatar.csdn.net/3/B/3/1_davebobo.jpg" ,imageView,handler).start(); } }

运行模拟器查看效果
Android实战之旅 006Android中的HTTP通信

文章图片

创建URL对象, 通过HttpURLConnection设置请求的方式, 得到读入流放到缓存区中, 通过webView加载本地的页面信息。

案例二: 通过网络请求下载图片到本地
在activity_main.xml中将WebView设置为隐藏, 添加 ImageView

< WebView android:visibility= " gone" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:id= " @ + id/webView" android:layout_alignParentTop= " true" android:layout_alignParentLeft= " true" android:layout_alignParentStart= " true" android:layout_alignParentRight= " true" android:layout_alignParentEnd= " true" android:layout_alignParentBottom= " true" /> < ImageView android:layout_width= " 200dp" android:layout_height= " 200dp" android:id= " @ + id/imageView" android:layout_centerVertical= " true" android:layout_centerHorizontal= " true" />

创建DownImage继承自Thread

package com.davebobo.http_01; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; import android.os.Handler; import android.widget.ImageView; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * Created by DaveBobo on 2016/10/9. */ public class DownImage extends Thread{privateString url; private ImageView imageView; private Handler handler; public DownImage(String url, ImageView imageView, Handler handler){ this.url = url; this.imageView = imageView; this.handler = handler; }@ Override public void run() { super.run(); try { URL httpUrl = new URL(url); try { HttpURLConnection conn = (HttpURLConnection)httpUrl.openConnection(); conn.setReadTimeout(5000); conn.setRequestMethod(" GET" ); conn.setDoInput(true); InputStream in = conn.getInputStream(); FileOutputStream out = null; File downloadFile = null; String fileName = String.valueOf(System.currentTimeMillis()); //判断SD卡是否存在 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File parent = Environment.getExternalStorageDirectory(); //获取SD目录 downloadFile = new File(parent,fileName); out = new FileOutputStream(downloadFile); }byte[] b = new byte[2*1024]; int len; if (out!= null){ while ((len = in.read(b))!= -1){ out.write(b,0,len); } }final Bitmap bitmap = BitmapFactory.decodeFile(downloadFile.getAbsolutePath()); handler.post(new Runnable() { @ Override public void run() { imageView.setImageBitmap(bitmap); } }); } catch (IOException e) { e.printStackTrace(); } } catch (MalformedURLException e) { e.printStackTrace(); } } }

在MainActivity中初始化ImageView组件并调用start()方法。

package com.davebobo.http_01; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.webkit.WebView; import android.widget.ImageView; public class MainActivity extends AppCompatActivity {private WebView webView; private Handler handler = new Handler(); private ImageView imageView; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); webView = (WebView) findViewById(R.id.webView); imageView = (ImageView) findViewById(R.id.imageView); //new VisitWeb(" https://www.baidu.com/" ,webView,handler).start(); new DownImage(" http://avatar.csdn.net/3/B/3/1_davebobo.jpg" ,imageView,handler).start(); } }


运行效果
Android实战之旅 006Android中的HTTP通信

文章图片


二、通过HttpURLConnection传递post, get参数
1.服务器端

首先我们使用myeclipse创建j2ee WEB项目, 项目名称为web
Android实战之旅 006Android中的HTTP通信

文章图片


新建MyServlet的servlet程序, 重写doGet和doPost方法, 为了简单重写一个方法即可。

package com.davebobo.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); }public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String name = request.getParameter(" name" ); String age = request.getParameter(" age" ); PrintWriter out = response.getWriter(); out.println(" name= " + name+ " age= " + age); System.out.println(" name= " + name); System.out.println(" age= " + age); }}

在index.jsp中添加form表单通过action访问servlet

< form action= " servlet/MyServlet" method= " get" > name:< input type= " text" name= " name" > < br/> age:< input type= " text" name= " age" > < br/> submit:< input type= " submit" value= " submit" > < br/> < /form>

运行效果
Android实战之旅 006Android中的HTTP通信

文章图片


2 客户端
当我们的服务器端搭建成功后, 开始编写客户端, 新建一个RegistActivity继承自AppCompatActivity

package com.davebobo.http_01; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; /** * Created by DaveBobo on 2016/10/26. */ public class RegistActivity extends AppCompatActivity {private EditText name; privateEditText age; private Button regist; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.regist); name = (EditText) findViewById(R.id.name); age = (EditText)findViewById(R.id.age); regist = (Button) findViewById(R.id.regist); regist.setOnClickListener(new View.OnClickListener() { @ Override public void onClick(View v) { String url = " http://192.168.1.100:8080/web/servlet/MyServlet" ; new HttpTherad1(url,name.getText().toString(),age.getText().toString()).start(); } }); } }

RegistActivity对应的布局文件regist.xml为:

< ?xml version= " 1.0" encoding= " utf-8" ?> < RelativeLayout xmlns:android= " http://schemas.android.com/apk/res/android" xmlns:tools= " http://schemas.android.com/tools" android:layout_width= " match_parent" android:layout_height= " match_parent" android:paddingBottom= " @ dimen/activity_vertical_margin" android:paddingLeft= " @ dimen/activity_horizontal_margin" android:paddingRight= " @ dimen/activity_horizontal_margin" android:paddingTop= " @ dimen/activity_vertical_margin" tools:context= " com.davebobo.http_01.MainActivity" > < RelativeLayout android:id= " @ + id/name_layout" android:layout_width= " fill_parent" android:layout_height= " wrap_content" > < TextView android:id= " @ + id/name_textView" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:layout_alignParentLeft= " true" android:layout_centerInParent= " true" android:text= " name" /> < EditText android:layout_toRightOf= " @ id/name_textView" android:id= " @ + id/name" android:layout_width= " fill_parent" android:layout_height= " wrap_content" /> < /RelativeLayout> < RelativeLayout android:layout_below= " @ id/name_layout" android:id= " @ + id/age_layout" android:layout_width= " fill_parent" android:layout_height= " wrap_content" > < TextView android:id= " @ + id/age_textView" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:layout_alignParentLeft= " true" android:layout_centerInParent= " true" android:text= " age" /> < EditText android:id= " @ + id/age" android:layout_toRightOf= " @ id/age_textView" android:layout_width= " fill_parent" android:layout_height= " wrap_content" /> < /RelativeLayout> < Button android:id= " @ + id/regist" android:layout_below= " @ id/age_layout" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:text= " regist" /> < /RelativeLayout>


由于我们需要进行网络操作单独新建一个线程类HttpTherad1编写相关的get和post方法。

package com.davebobo.http_01; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * Created by DaveBobo on 2016/10/26. */ public class HttpTherad1 extends Thread{privateString url; privateString name; privateString age; public HttpTherad1(String url,String name,String age){ this.url = url; this.name = name; this.age = age; }privatevoid doGet(){ url = url+ " ?name= " + name+ " & age= " + age; //get方式只能通过url进行传参 try { URL httpUrl = new URL(url); try { HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection(); conn.setRequestMethod(" GET" ); conn.setReadTimeout(5000); BufferedReader reader= new BufferedReader(new InputStreamReader(conn.getInputStream())); String str; StringBuffer sb = new StringBuffer(); while ((str = reader.readLine())!= null ){ sb.append(str); } System.out.println(" result:" + sb.toString()); } catch (IOException e) { e.printStackTrace(); } } catch (MalformedURLException e) { e.printStackTrace(); }}private void doPost(){try { URL httpUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection(); conn.setRequestMethod(" POST" ); conn.setReadTimeout(5000); OutputStream out = conn.getOutputStream(); String content = " name= " + name+ " & age= " + age; out.write(content.getBytes()); BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); StringBuffer sb = new StringBuffer(); String str; while ((str= reader.readLine())!= null){ sb.append(str); } System.out.println(sb.toString()); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }} @ Override public void run() { super.run(); //doGet(); doPost(); } }

客户端请求get方法 post方法测试:
Android实战之旅 006Android中的HTTP通信

文章图片
Android实战之旅 006Android中的HTTP通信

文章图片


服务器打印出结果:
Android实战之旅 006Android中的HTTP通信

文章图片


由此我们可以看出doGet是通过URL方式进行发送, 而doPost是通过OutputStream的方式进行发送。那有什么区别呢? 一般地如果发送的实体数据比较少的话我们使用doGet方式进行发送, 这种方式对发送的实体数据是有一定的限制的( 几K) , 如果发送的数据量比较大时我们使用doPost。另外通过doGet方式发送我们通过url将数据暴露出来, 如果想安全的话也采用doPost通过实体去发送。
三、httpClient的使用 Java给我们提供了一套访问网络的API但不远远足够我们开发应用程序, 在这里我们使用httpClient进行GET和POST请求。在API 23中, Google已经移除了Apache HttpClient相关的类 。谷歌推荐使用HttpUrlConnection, 如果要继续使用Apache HttpClient, 导入相应的jar包并需要在
Eclipse下libs里添加org.apache.http.legacy.jar,
Android studio里在相应的module下的build.gradle中加入如下即可。
导入我们用到的jar包httpclient-4.5.2.jar httpcore-4.4.4.jar


android { useLibrary ' org.apache.http.legacy' }android { packagingOptions { exclude ' META-INF/DEPENDENCIES.txt' exclude ' META-INF/LICENSE.txt' exclude ' META-INF/NOTICE.txt' exclude ' META-INF/NOTICE' exclude ' META-INF/LICENSE' exclude ' META-INF/DEPENDENCIES' exclude ' META-INF/notice.txt' exclude ' META-INF/license.txt' exclude ' META-INF/dependencies.txt' exclude ' META-INF/LGPL2.1' } }

新建HttpClientThread继承自Thread

package com.davebobo.http_01; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; /** * Created by DaveBobo on 2016/10/26. */ public class HttpClientThread extends Thread{privateString url; privateString name; privateString age; public HttpClientThread(String url){ this.url= url; } public HttpClientThread(String url,String name,String age){ this.url = url; this.name = name; this.age = age; }private void dohttpClientGet(){ //创建HttpGet对象 HttpGet httpGet = new HttpGet(url); //创建HttpClient对象 HttpClient client = new DefaultHttpClient(); HttpResponse response; try { //发送请求 response = client.execute(httpGet); //判断类型 if (response.getStatusLine().getStatusCode()= = HttpStatus.SC_OK){ //取出服务器返回的数据 String content = EntityUtils.toString(response.getEntity()); System.out.println(" content-----> " + content); } } catch (IOException e) { e.printStackTrace(); } }public void doHttpClientPost(){HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(url); //通过NameValuePair存储数据 ArrayList< NameValuePair> list = new ArrayList< NameValuePair> (); list.add(new BasicNameValuePair(" name" ,name)); list.add(new BasicNameValuePair(" age" ,age)); try { //设置要发送的数据 post.setEntity(new UrlEncodedFormEntity(list)); try { HttpResponse response = client.execute(post); if (response.getStatusLine().getStatusCode()= = HttpStatus.SC_OK){ String content = EntityUtils.toString(response.getEntity()); } } catch (IOException e) { e.printStackTrace(); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } @ Override public void run() { //dohttpClientGet(); doHttpClientPost(); } }


RegistActivity.java中的onCreate方法测试

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.regist); name = (EditText) findViewById(R.id.name); age = (EditText)findViewById(R.id.age); regist = (Button) findViewById(R.id.regist); regist.setOnClickListener(new View.OnClickListener() { @ Override public void onClick(View v) { String url = " http://192.168.1.100:8080/web/servlet/MyServlet" ; //new HttpTherad1(url,name.getText().toString(),age.getText().toString()).start(); //url = url+ " ?name= " + name.getText().toString()+ " & age= " + age.getText().toString(); //new HttpClientThread(url).start(); new HttpClientThread(url,name.getText().toString(),age.getText().toString()).start(); } }); }

Android实战之旅 006Android中的HTTP通信

文章图片
Android实战之旅 006Android中的HTTP通信

文章图片

Android实战之旅 006Android中的HTTP通信

文章图片


四、Http多线程下载和文件上传 案例一: Http多线程下载

首先我们在服务器端拷贝一张图片并重新发布。
Android实战之旅 006Android中的HTTP通信

文章图片


新建DownLoadActivity.继承自AppCompatActivity

package com.davebobo.http_01; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; /** * Created by DaveBobo on 2016/10/26. */ public class DownLoadActivity extends AppCompatActivity {private Button button; private TextView textView; private int count = 0; //更新UI操作 private Handler handler = new Handler(){ publicvoid handleMessage(android.os.Message msg){ int result = msg.what; count+ = result; if (count= = 3){ textView.setText(" download success!" ); }}; }; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.download); button = (Button) findViewById(R.id.button1); textView = (TextView) findViewById(R.id.textView); button.setOnClickListener(new View.OnClickListener() { @ Override public void onClick(View v) {new Thread(){ public voidrun(){ DownLoad load = new DownLoad(handler); load.downLoadFile(" http://192.168.1.100:8080/web/head.jpg" ); } }.start(); } }); } }

多线程下载业务处理在DownLoad.java中

package com.davebobo.http_01; import android.os.Environment; import android.os.Handler; import android.os.Message; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Created by DaveBobo on 2016/10/26. */ public class DownLoad {private Handler handler; public DownLoad(Handler handler){ this.handler = handler; } //创建线程池 private Executor threadPool = Executors.newFixedThreadPool(3); static class DownLoadRunnable implements Runnable{ private String url; private String fileName; private long start; private long end; private Handler handler; DownLoadRunnable(String url,String fileName,long start,long end,Handler handler){ this.url = url; this.fileName = fileName; this.start = start; this.end = end; this.handler = handler; } @ Override public void run() { try { URLhttpUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection)httpUrl.openConnection(); conn.setRequestMethod(" GET" ); conn.setReadTimeout(5000); conn.setRequestProperty(" Range" , " bytes= " + start + " -" + end); RandomAccessFile access = new RandomAccessFile(new File(fileName)," rwd" ); access.seek(start); InputStream in = conn.getInputStream(); byte[] b= new byte[1024*4]; int len= 0; while ((len = in.read(b))!= -1){ access.write(b,0,len); } if (access!= null){ access.close(); } if (in!= null){ in.close(); }Message message = new Message(); message.what = 1; handler.sendMessage(message); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } public void downLoadFile(String url){try { URLhttpUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection)httpUrl.openConnection(); conn.setRequestMethod(" GET" ); conn.setReadTimeout(5000); /** * 11/3 32 * 第一个线程 0-2 * 第二个线程 3-5 * 第三个线程 6-10 */ int count = conn.getContentLength(); int block = count/3; String fileName = getFileName(url); File parent = Environment.getExternalStorageDirectory(); File fileDownLoad = new File(parent,fileName); for (int i= 0; i< 3; i+ + ){ long start = i*block; long end = (i+ 1)*block-1; if (i= = 2){ end = count; } DownLoadRunnable runnable = new DownLoadRunnable(url,fileDownLoad.getAbsolutePath(),start,end,handler); threadPool.execute(runnable); ; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }public String getFileName(String url){ return url.substring(url.lastIndexOf(" /" )+ 1); } }

布局配置文件很简单

< ?xml version= " 1.0" encoding= " utf-8" ?> < RelativeLayout xmlns:android= " http://schemas.android.com/apk/res/android" xmlns:tools= " http://schemas.android.com/tools" android:layout_width= " match_parent" android:layout_height= " match_parent" android:paddingBottom= " @ dimen/activity_vertical_margin" android:paddingLeft= " @ dimen/activity_horizontal_margin" android:paddingRight= " @ dimen/activity_horizontal_margin" android:paddingTop= " @ dimen/activity_vertical_margin" tools:context= " com.davebobo.http_01.MainActivity" > < Button android:id= " @ + id/button1" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:layout_alignParentTop= " true" android:layout_centerInParent= " true" android:text= " Button" /> < TextView android:id= " @ + id/textView" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:layout_centerInParent= " true" /> < /RelativeLayout>

在AndroidManifest.xml中配置默认加载的Activity

< activity android:name= " .DownLoadActivity" > < intent-filter> < action android:name= " android.intent.action.MAIN" /> < category android:name= " android.intent.category.LAUNCHER" /> < /intent-filter> < /activity>

运行测试结果
Android实战之旅 006Android中的HTTP通信

文章图片
Android实战之旅 006Android中的HTTP通信

文章图片

知识总结:
1.Http协议字段Range “bytes= ”+ start+ " -' + end
2.RandomAccessFile设置写入的位置
3.开启线程发送网络请求


案例二: http文件上传
服务器端:
在这里我们需要用到servlet3.0的新特性, 不需要添加额外的jar包, servlet3.0对文件的操作做了很好的支持。开发Servlet3的程序需要一定的环境支持。Servlet3是Java EE6规范的一部分, MyEclipse10和Tomcat7都提供了对Java EE6规范的支持。Tomcat需要Tomcat7才支持Java EE6, Tomcat7需要使用JDK6。
使用Myeclipse新建servlet命名为AnnotationUpload

Android实战之旅 006Android中的HTTP通信

文章图片


对上图进行如下操作: 如果要使用Servlet3.0新特性——采用注释的方式该servlet接受的请求路径, 则取消上图被红框框定的复选框( 取消后web.xml文件中将不会含有创建的Servlet的一些配置信息, 此处取消该复选选中项) ; 否则直接点击“Finish”按钮即可。至此3.0版的Servlet创建成功。将该Servlet修改为如下代码:
package com.davebobo.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; /** * 使用注解描述Servlet * @ author DaveBobo *//** * 注解WebServlet用来描述一个Servlet * 属性name描述Servlet的名字,可选 * 属性urlPatterns定义访问的URL,或者使用属性value定义访问的URL.(定义访问的URL是必选属性) */ @ WebServlet(name= " AnnotationUpload" ,urlPatterns= " /AnnotationUpload" ) @ MultipartConfig( location = " D:\\\\" ) public class AnnotationUpload extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Part part = request.getPart(" file" ); part.write(" upload.jpg" ); response.setCharacterEncoding(" UTF-8" ); PrintWriter out = response.getWriter(); out.print(" upload success" ); System.out.println(" upload success" ); }}

新建upload.jsp

< %@ page language= " java" import= " java.util.*" pageEncoding= " ISO-8859-1" %> < % String path = request.getContextPath(); String basePath = request.getScheme()+ " ://" + request.getServerName()+ " :" + request.getServerPort()+ path+ " /" ; %> < !DOCTYPE HTML PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN" > < html> < head> < base href= " < %= basePath%> " > < title> My JSP ' upload.jsp' starting page< /title> < meta http-equiv= " pragma" content= " no-cache" > < meta http-equiv= " cache-control" content= " no-cache" > < meta http-equiv= " expires" content= " 0" > < meta http-equiv= " keywords" content= " keyword1,keyword2,keyword3" > < meta http-equiv= " description" content= " This is my page" > < !-- < link rel= " stylesheet" type= " text/css" href= " styles.css" > --> < /head> < body> < form action= " AnnotationUpload" method= " post" enctype= " multipart/form-data" > < input type = " file" name= " file" /> < br> < input type= " submit" value= " submit" /> < br> < /form> < /body> < /html>

运行测试图片传到指定文件夹中。
【Android实战之旅 006Android中的HTTP通信】
Android实战之旅 006Android中的HTTP通信

文章图片



客户端:
UploadThread.java

package com.davebobo.http_01; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * Created by DaveBobo on 2016/10/26. */ public class UploadThread extends Thread{private String fileName; private String url; publicUploadThread(String url,String fileName){ this.url = url; this.fileName = fileName; } @ Override public void run() { //上传表单分割线的描述符 String boundary = " ---------------------------7e01ee305f069a" ; //Content-Type String reqCon = " -----------------------------7e01ee305f069a" ; //请求正文比Content-Type多两个- String prefix = " --" ; String end= " \\r\\n" ; try { URL httpUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection(); conn.setRequestMethod(" POST" ); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestProperty(" Content-Type" , " multipart/form-data; boundary= " + boundary); DataOutputStream out = new DataOutputStream(conn.getOutputStream()); out.writeBytes(prefix+ boundary+ end); out.writeBytes(" Content-Disposition: form-data; " + " name= \\" file\\" ; filename= \\" " + " head.jpg" + " \\" " + end); //实体数据 out.writeBytes(end); FileInputStream fileInputStream = new FileInputStream(new File(fileName)); byte[] b = new byte[1024*4]; int len; while ((len= fileInputStream.read(b))!= -1){ out.write(b,0, len); } out.writeBytes(end); out.writeBytes(prefix + boundary + prefix + end); out.flush(); BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); StringBuffer sb = new StringBuffer(); String str; while ((str= reader.readLine())!= null){ sb.append(str); System.out.println(" respose:" + sb.toString()); if (out!= null){ out.close(); } if (reader!= null){ reader.close(); } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }


这儿需要注意我们拼写上传表单分割线的描述符时, 需要使用IE的开发人员工具。
Android实战之旅 006Android中的HTTP通信

文章图片

UploadActivity.java

package com.davebobo.http_01; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import java.io.File; /** * Created by DaveBobo on 2016/10/27. */ public class UploadActivity extends AppCompatActivity {private Button button; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.upload); button = (Button) findViewById(R.id.button2); button.setOnClickListener(new View.OnClickListener() { @ Override public void onClick(View v) { String url= " http://192.168.1.100:8080/web/AnnotationUpload" ; File file = Environment.getExternalStorageDirectory(); File fileAbs = new File(file," head.jpg" ); String fileName= fileAbs.getAbsolutePath(); UploadThread thread = new UploadThread(url,fileName); thread.start(); } }); } }

布局文件upload.xml, 就一个button

< ?xml version= " 1.0" encoding= " utf-8" ?> < LinearLayout xmlns:android= " http://schemas.android.com/apk/res/android" android:layout_width= " match_parent" android:layout_height= " match_parent" > < Button android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:text= " Button" android:id= " @ + id/button2" /> < /LinearLayout>



修改默认Activity为UploadActivity,运行程序成功。
httpclient简单实现
这里我们要将httpmime-4.5.2.jar包导入工程, 修改UploadThread.java为

package com.davebobo.http_01; import android.os.Environment; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.FileBody; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * Created by DaveBobo on 2016/10/26. */ public class UploadThread extends Thread{private String fileName; private String url; publicUploadThread(String url,String fileName){ this.url = url; this.fileName = fileName; }private void httpUpload(){ //上传表单分割线的描述符 String boundary = " ---------------------------7e01ee305f069a" ; //Content-Type String reqCon = " -----------------------------7e01ee305f069a" ; //请求正文比Content-Type多两个- String prefix = " --" ; String end= " \\r\\n" ; try { URL httpUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection(); conn.setRequestMethod(" POST" ); conn.setDoOutput(true); conn.setDoInput(true); conn.setRequestProperty(" Content-Type" , " multipart/form-data; boundary= " + boundary); DataOutputStream out = new DataOutputStream(conn.getOutputStream()); out.writeBytes(prefix+ boundary+ end); out.writeBytes(" Content-Disposition: form-data; " + " name= \\" file\\" ; filename= \\" " + " head.jpg" + " \\" " + end); //实体数据 out.writeBytes(end); FileInputStream fileInputStream = new FileInputStream(new File(fileName)); byte[] b = new byte[1024*4]; int len; while ((len= fileInputStream.read(b))!= -1){ out.write(b,0, len); } out.writeBytes(end); out.writeBytes(prefix + boundary + prefix + end); out.flush(); BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); StringBuffer sb = new StringBuffer(); String str; while ((str= reader.readLine())!= null){ sb.append(str); System.out.println(" respose:" + sb.toString()); if (out!= null){ out.close(); } if (reader!= null){ reader.close(); } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }private void uploadHttpClient(){HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(url); MultipartEntity muti = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); File parent = Environment.getExternalStorageDirectory(); File fileAbs = new File(parent," head.jpg" ); FileBody fileBody = new FileBody(fileAbs); muti.addPart(" file" , fileBody); post.setEntity(muti); try { HttpResponse response = client.execute(post); if (response.getStatusLine().getStatusCode() = = HttpStatus.SC_OK){ System.out.println(EntityUtils.toString(response.getEntity())); } } catch (IOException e) { e.printStackTrace(); } } @ Override public void run() { uploadHttpClient(); } }

在这里有一点小插曲, 我使用的httpClient版本为httpcomponents-client-4.5.2, Android Studio提示错误:

10-27 10:33:06.980 24750-24826/com.davebobo.http_01 E/AndroidRuntime: FATAL EXCEPTION: Thread-249 Process: com.davebobo.http_01, PID: 24750 java.lang.NoSuchFieldError: No static field INSTANCE of type Lorg/apache/http/message/BasicHeaderValueFormatter; in class Lorg/apache/http/message /BasicHeaderValueFormatter; or its superclasses (declaration of ' org.apache.http.message.BasicHeaderValueFormatter'

解决方式: 下载使用 httpclient-4.3.1即可。
替换为httpclient-4.3.1后测试运行成功。
【Android实战之旅 006】Android中的HTTP通信 配套的资源下载


    推荐阅读