【Android实战之旅 006】Android中的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{

    private  String 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();
    }
}
运行模拟器查看效果

创建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{

    private  String 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();
    }
}

运行效果


二、通过HttpURLConnection传递post,get参数

1.服务器端

首先我们使用myeclipse创建j2ee WEB项目,项目名称为web


 新建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>
运行效果


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;
    private  EditText 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{

    private  String url;
    private  String name;
    private  String age;

    public HttpTherad1(String url,String name,String age){
        this.url = url;
        this.name = name;
        this.age = age;
    }

    private  void 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方法测试:

    

服务器打印出结果:


由此我们可以看出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{

    private  String url;
    private  String name;
    private  String 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();
            }
        });
    }
   


四、Http多线程下载和文件上传

案例一: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(){
        public  void 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 void  run(){
                        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 {
                URL  httpUrl = 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 {
            URL  httpUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection)httpUrl.openConnection();
            conn.setRequestMethod("GET");
            conn.setReadTimeout(5000);
            /**
             * 11/3 3  2
             * 第一个线程 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>
运行测试结果

  

知识总结:

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

对上图进行如下操作:如果要使用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>
运行测试图片传到指定文件夹中。


客户端:

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;

    public  UploadThread(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的开发人员工具。


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;

    public  UploadThread(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通信 配套的资源下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

余莫星辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值