Android06_getpost提交_文件上传_多线程下载

Posted 抓根宝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android06_getpost提交_文件上传_多线程下载相关的知识,希望对你有一定的参考价值。

提交数据有中文的话,一定要用URLEncoder进行编码

1,Get方式提交数据

1.1案例:发送QQ账号和密码

①把信息通过网络请求发送到服务器

②在服务端数据库查询账号密码是否存在

③服务器返回具体的信息

 

1.1.1,Web端的实现

①创建一个Servlet接收客户端请求

②获取请求数据

③封装成对象传入数据库中(因为主要是练习android的网络请求,所以这里可以简化一下,直接判断两个数据是否相等)

④查询数据库返回结果  

//通过response对象返回结果,response.getOutputStream().Write(xxx.getBytes());

⑤创建一个jsp页面提供给用户进行登录(测试用)

1.1.2,手机端实现

①布局界面:账号,密码,提交框,状态框

②找到控件,设置点击事件,获取信息

③创建网络路径,因为是GET提交,所以可以把请求参数放在路径后面,进行字符串拼接.

④创建URL,获取HttpURLConnection对象

⑤设置请求方式,超时时间

⑥获取状态码,拿到服务器返回的输入流

⑦对输入流进行转换,转换成字符串(其实这里返回的就一个是否成功信息)

⑧通过消息处理器把消息发送到主线程中

⑨主线程对信息进行处理

 

GET请求的优点:使用方便.

缺点:账号密码都是拼接在路径之后,并且有最大长度限制(IE最长只能写1K,http协议规定是4K)

两者请求信息的区别

GET:

POST:必须指定请求的类型:Content-Type(数据类型),application/x-www-form-urlencode 经过url编码的数据

必须指定提交数据的程度Content-Length(字符长度)POST请求是以流的形式把数据写给服务器,所以必须要告诉服务器写多长的数据

2,POST请求

优点:数据安全,数据不是url后面拼接,而是通过流的方式写给服务器,并且数据长度不受限制

缺点:编写麻烦

2.1,服务端页面

基本等同于GET,除了jsp中的form表单设置外一样

2.2,手机端代码

①布局界面:账号,密码,提交框,状态框

②找到控件,设置点击事件,获取信息

③创建网络路径,不需要进行拼接

④设置请求方式和超时时间requestMethod(“POST”)//必须大写

⑤设置http请求数据的类型为表单类型,设置请求参数(不记得参数怎么写直接看浏览器的请求头即可)

conn.setRequestProperty(“Content-type”,”application/x-www-form-urlencode”);

⑥设置提交数据的长度

conn.setRequestProperty(“Content-Length”,String.valueof(字符串.length));

⑦指定写给服务器的数据

conn.setDoOutput(true);

//在获取状态码之前写出

conn.getOutputStream().write(字符串.getBytes());

// 后面等同于GET提交

 

3.中文乱码问题,安卓默认用的utf-8码表,tomcat默认使用的iso-8859-1码表(其它服务器不一样)

tomcat如果发现字符在自身的码表找不到,就会采用本地的码表

解决方法①服务器在反馈信息的话,在转换成字符数组的时候指定utf-8码表即可

     ②客户端在转换输入流的时候指定gb2312码表即可

 

4.中文乱码解决的最根本的方法,

查看字符串乱码的样子

创建一个字符串通过String的构造进行转码,解码,

通过getBytes(码表)转换成二进制,通过new String(字节数,码表),保持一致就可以正确的显示出来

但是如果两者不一致就会出现乱码,可以凭借这一点查看你设定的码表在不同码表下的状态.

当操作文件出现中文乱码的时候,不要去修改文件里任意一个字符,加一个空格也不行.因为修改了就会代表码表映射失败,再把码表修改回来也无法正确的显示

 

5,提交数据的中文问题,客户端有中文就要进行URLEncoder.encode(字符串,码表)编码

                    服务端接收到了这个编码(ISO-8859-1)之后的数据,也要进行转码

new String(字符串.getBytes(“iso-8859-1”),”utf-8”)

GET,POST提交都是面向http协议的过程来进行编程,必须对Http的内部执行过程很熟悉,该怎么设置请求头.

 

注意,上面这两种方式是面向过程的思想,一切都有自己完成,熟悉规则,知道该怎么做.

 

6,Android下的开源项目httpclient提交数据到服务器(apache的开源jar)(稳定性很高,是一种面向对象的思想(使用的时候相当于用户在进行登录的流过程)(主要是帮助实现Post操作,GET操作本来就简单)

httpClient相当于一个轻量级的浏览器

步骤:1,打开浏览器

2,输入地址(数据)

3,敲回车

①打开浏览器

(一般Android的接口都有实现类,命名规则BaseXXX,SimpleXXX,DefaultXXX)

HttpClient client = new DefaultHttpClient();

②输入地址或数据得到请求方式

HttpGet httpGet = new HttpGet(Path);

//如果是POST请求就是HttpPost httppost = new HttpPost(path);

//List<NameValuesPair(键值对)> parameters = new ArrayList();

//Parameters.add(new BasicNameValuePair(“名称”,对应的值));

//Parameters.add(new BaseNameValuesPair(“名称”,对应的值));

//采用URL转码之后的数据实体

//把数据添加到表单里,相当于向一个from表单里添加数据,httppost.setEntity(设置数据的实体) (new UrlEncodingFormEntity(parameters,””utf-8”));

③敲回车,得到响应头

HttpResponse response= Client.execute(httpGet);

④获取服务器返回的数据

response.getAllHeaders();//获取返回所有的头信息

response.getStatusLine().getStatuCode();通过状态行获取状态码

⑤获取服务器返回的输入流

response.getEntity()(获取返回所有的数据实体).getContent()(返回一个inputStream);

 

7,采用开源框架async http android进行提交数据(底层就是前面提到的提交数据方式,采用HttpClient或者面向http协议发送的请求,android的异步请求进行封装,所以不需要创建handler消息转发了)

GET请求

①获取路径

②创建AsycHttpClient对象,里面提供了一套示例代码(可以直接拿来用)

String path ="http://192.168.16.80:8080/Android/servlet/PhoneLogin?username=" +

URLEncoder.encode(username,"utf-8") + "&password=" + URLEncoder.encode(password,"utf-8");

AsyncHttpClient client = new AsyncHttpClient();

调用clinet.get方法即可,第一个参数是路径,第二个参数 AsyncHttpResponseHandler(异步http响应对象消息转发),这里就不用再通过seendMessage转发消息即可

onSuccess请求成功时候调用

onFailure 请求失败的时候调用

POST请求

①指定请求路径

②指定异步的请求客户端,指定请求参数

AsyncHttpClient client = new AsyncHttpClient();

RequestParams params = new RequestParams();

     params.put("username", username);

     params.put("password", passwrod);

POST数据提交到服务器

client.post(path, params, new AsyncHttpResponseHandler())

AsyncHttpResonseHandler里面有两个方法

onSuccess请求成功时候调用

onFailure 请求失败的时候调用

应用:可以通过这一方式,写一个where循环不断访问网络,来抢票,秒杀之类的,不断下达下单指令即可.

 

8,上传文件到服务器

8.1服务端:

①创建一个jsp页面提供用户上传数据

Form method=post,一般的表单属于文本表单类型, 上传文件需要制定enctype=”multipart/form-data”并且需要额外的jar包支持,参考web工具类

Input type = file,name =filename;

②定义一个Servlet接收上传文件(直接用以前web的文件,懒得写)

 

8.2 客户端(使用开源框架,就是刚才那个框架,因为POST请求上传本质上是把文件转换成流的形式上传)

①创建路径输入框,上传确认项

②判断输入的路径是文件是否存在,是否长度大于0

RequestParams params 对象可以直接把文件put进去 params.put(“xxx”,文件对象);(好屌的框架)

 

9,多线程下载

9.1为什么多线程可以提高下载的速度:从服务上获取的资源变多,单位时间下载的速率就变快了

但是下载速度还是受到服务器上传的带宽和用户下载的带宽限制

多线程并发操作,网络请求

9.2 Android下多线程的下载操作

9.2.1现在 Web下创建测试

①在客户端本地创建一个空白的文件,文件的大小跟服务器的文件一样,通过:RandomAccessFile

②开启若干个线程下载服务器资源

③当所有的线程都下载完毕后,多线程的下载就结束了.

 

10,划分服务器资源

10.1

  //获取完服务器数据之后

①获取服务器返回的信息长度

HttpURLConnection conn

conn.getContentLength()返回

②通过RandomAccessFile 设置空文件raf = new RandomAccessFile(文件名称,模式(只读,可读可写));

//设置文件长度

raf.setLength(服务器端的文件长度)

raf.close();

③定义开启线程的数量, threadCount = 3;

给每个线程分配下载范围(文件长度/线程数量) blocksize-1

线程0 : 0>>blocksize-1

线程1:blocksize ->>2*blocksize-1;

线程2:blocksize*2 >> 3 * blocksize-1

线程N:blocksize*n >> (n+1)blocksize-1

最后一个线程结束位置应该是文件的末尾

N*blocksize>>>文件长度-1

 

10.2,

①创建一个自定义线程,继承Thread

②定义开始,结束位置,定义线程ID

③定义有参构造,使用该线程必须指定以上3个成员变量

④重写RUN方法

定义URL解析输入的path路径(通过url获取HttpURLConnection之后获取的输入流,获取的是整个文件的输入流,而不是每个线程想要下载的一部分)

HttpURLConnection conn

告诉服务器想要下载的范围

//通过设置请求头告诉服务器我要下载的范围,这个写法是固定的

Conn.setRequestProperty(“Range”,”bytes=” + startIndex + “-” + endIndex);

//创建RandomAccessFile 储存线程读取到的信息(RandomAccessFile会自己处理多线程并发的问题,这样就能保证文件的代码保持一致性)

Raf.seek(startIndex)//定义开始写的位置,每个线程写的位置不一样

//流对接,关流

 

额外:定义一个方法获取文件名(URL字符串进行截取即可)

http请求属于短链接,HttpURLConnection 不适合做成一个成员变量,它执行完了就关掉了.

 

11多线程断点续传功能实现

11.1定义一个成员变量currentPosition记录下载的进度,它代表当前线程的下载位置.

11.2 创建一个文件,保存currentPosition的信息,也就是下载进度

11.3 重新下载的时候,先读取文件中的currentPosition信息,设置开始下载的位置

11.4 要注意,如果有线程下载完毕之后,把文件标记为失效,同时做判断,如果开始长度大于结束长度,就不进行流对接即可

标记失效:重命名文件,修改文件后缀名(方便标记)即可,

最后如果所有进程就下完了,就删除所有文件

额外:使用FIleOutputStream 写出信息会先写到硬盘的缓存里,这时候如果断电了,可能数据还没写到底层存储设备中,就有可能数据没有写进去,再次启动断点续传就可能丢失断点

解决方法:使用RandomAccessFile raf = new RandomAccessFile(file,rwd);

rwd>>>>可读可写,并且每次写出都立即写到底层存储设备中

//但是这样做会更耗时间,对硬盘消耗稍微大一点.所以,自己选择,这里做练习,就用这种写法

//byte数组设置越大,对硬盘消耗也就越少,但是,断点下载丢失的数据也就越多

//大量的,频繁的使用流写出数据到硬盘,就会先写到缓存中,再写到硬盘中.

 

12移植到Android

①用户权限(如果写在 SD卡就需要权限)

②路径问题

③对每个线程增加一个进度条ProgressBar pb

pb.setMax,pb.setProgress

 

13开源框架Xutils

①创建HttpUtils

②调用download(path,下载目录,是否断点续传,下载的回调);

onLoading(long total, long current, boolean isUploading)

total文件总长度,current进度

 

 附上个人写的断点续传文件代码,大致框架出来了,有些小细节没有调整,比如当下载的是重复文件的时候,断点冲突之类,有时间再整理好了

package com.zzx.mutidown;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;

public class MainActivity extends Activity {
    private static int threadcount = 3;
    private static long startindex;
    private static  long endindex;
    private static String path;
    private static int len;
    private static String filename;
    private EditText et_ph;
    private Button bt_sub;
    private static ProgressBar pb_01;
    private static ProgressBar pb_02;
    private static ProgressBar pb_03;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        init();
        /**
         * 开启断点下载
         */
        bt_sub.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                 System.out.println(1111);
                onclick();
            }
        });
    
    }
    
    private void onclick() {
        new Thread(){
            public void run() {
                //获取路径
                String path = "http://192.168.16.80:8080/aaa.txt";
                if(TextUtils.isEmpty(path)){
                    System.out.println("请输入正确的内容");
                    return;
                }
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    //获取长度
                    len = conn.getContentLength();
                    //获取文件名
                    filename = path.substring(path.lastIndexOf("/")+1);
                    RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath() +"/" + filename, "rwd");
                    raf.setLength(len);
                    //这里记得关流
                    raf.close();
                    //划分每一个子线程的长度
                    for (int i = 0; i < threadcount; i++) {
                        startindex = i*len/threadcount;
                    
                        if(i<threadcount-1){
                        endindex = (i+1)*len/threadcount-1;
                        }else{
                            endindex = len-1;    
                        }
                        if(i==0){
                            pb_01.setMax((int)endindex);
                        }else if(i==1){
                            pb_02.setMax((int)endindex);
                        }else if(i==2){
                            pb_03.setMax((int)endindex);
                        }
                        MyThread mt = new MyThread(startindex, endindex, i);
                        mt.start();
                    }
                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "网络异常", 0).show();
                }
            };
            
        }.start();
    }

    /**
     * 加载控件
     */
    private void init() {
        et_ph = (EditText) findViewById(R.id.urlpath);
        bt_sub = (Button) findViewById(R.id.submit);
        pb_01 = (ProgressBar) findViewById(R.id.progressBar1);
        pb_02 = (ProgressBar) findViewById(R.id.progressBar2);
        pb_03 = (ProgressBar) findViewById(R.id.progressBar3);
    }
    /**
     * 这里是自定义文件下载子线程
     * @author msi
     *
     */
    static class MyThread extends Thread{
        /**
         * 初始化线程类
         * @param startindex    开始的位置
         * @param endindex    结束的位置
         * @param breakpoint    断点的位置
         */
        private long startindex;
        private  long endindex;
        private static int deleteindex = threadcount;
        private  long i ;
        private  long breakpoint;
        public MyThread(long startindex,long endindex,int i){
            this.startindex = startindex;
            this.endindex = endindex;
            this.i = i;
            
        }
        public void run() {
            try {
                File file = new File(Environment.getExternalStorageDirectory().getPath() +"/" +i + ".properties");
                //获取断点
                if(!file.exists()||file.length()<=0){
                     breakpoint = startindex;
                        if(i==0){
                            pb_01.setProgress(0);
                        }else if(i==1){
                            pb_02.setProgress(0);
                        }else if(i==2){
                            pb_03.setProgress(0);
                        }
                }else{
                BufferedReader br = new BufferedReader(new FileReader(file));
                breakpoint = Integer.parseInt(br.readLine());
                pbset();
                }
                System.out.println("Thread" + i + "开启了,它下载的范围是" + breakpoint + ">>" +endindex );
                
                RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath() +"/" +filename, "rwd");
                raf.seek(breakpoint);
                String path = "http://192.168.16.80:8080/aaa.txt";
                //1,访问网络
                URL url = new URL(path);
                //2,获取链接
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                //3,设置请求头信息
                conn.setRequestProperty("Range", "bytes=" + breakpoint + "-" + endindex);
                //4,获取已经被创建的等大小文件,开始写信息
                //5,获取输入流输出流
                InputStream is =conn.getInputStream();
                //6,流对接
                byte[] arr = new byte[2];
                int len = 0;
                while((len = is .read(arr))!=-1){
                    Thread.sleep(500);
                    raf.write(arr,0,len);
                    breakpoint+=len;
                    //把断点信息写到文件中去
                    RandomAccessFile bp = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath() +"/" + i + ".properties", "rwd");
                    System.out.println(i + "线程的断点是" + breakpoint);
                    bp.write((breakpoint + "").getBytes());
                    bp.close();
                    pbset();
                }
                System.out.println(i + "线程执行完了");
                synchronized (MainActivity.class) {
                    deleteindex--;
                    System.out.println(deleteindex);
                    if(deleteindex==0){
                        System.out.println(deleteindex);
                        for(int i=0;i<3;i++){
                        File filei = new File(Environment.getExternalStorageDirectory().getPath() +"/" +i + ".properties");
                        filei.delete();
                        }
                    }
                    System.out.println("下完完毕");
                }
                //写完之后关流
                is.close();
                raf.close();
            } catch (Exception e) {
                System.out.println("子线程异常");
            }
            super.run();
        }
        /**
         * 设置进度
         */
        public void pbset() {
            if(i==0){
                pb_01.setProgress((int)breakpoint);
            }else if(i==1){
                pb_02.setProgress((int)breakpoint);
            }else if(i==2){
                pb_03.setProgress((int)breakpoint);
            }
        }
    }
    
}

 

以上是关于Android06_getpost提交_文件上传_多线程下载的主要内容,如果未能解决你的问题,请参考以下文章

AJAX 上传后 $_FILES 为空

TP文件上传

CTFHub_文件上传

CTFHub_文件上传

CTFHub_文件上传

CTFHub_文件上传