zoukankan      html  css  js  c++  java
  • 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);
                }
            }
        }
        
    }
  • 相关阅读:
    用Python查找数组中出现奇数次的那个数字
    python之路--MySQL多表查询
    python之路--MySQl单表查询
    python之路--MySQL 库,表的详细操作
    python之路--MySQL数据库初识
    python之路--线程的其他方法
    python之路--关于线程的一些方法
    python之路--管道, 事件, 信号量, 进程池
    python之路--进程内容补充
    python之路--操作系统介绍,进程的创建
  • 原文地址:https://www.cnblogs.com/adventurer/p/5536322.html
Copyright © 2011-2022 走看看