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提交_文件上传_多线程下载的主要内容,如果未能解决你的问题,请参考以下文章