一、 方式使用jdk中自带的api 实现发请求,并解析(重要):
- GETt方式下的请求, 如果 带过去给服务器端的数据有中文的, 那么这些中文的数据就必须要经过url 编码, 改成 那种 %EB% 这种形式,

服务器端还要做一个事儿, 要使用 这里 编码的 时候的 码表 进行解码

- POST方式下的请求

经过以上分析, 发现 post 时 多了 两个请求头 , 并且 请求的方式 以及 参数 带过去的方式也是不一样的
a、在 客户端的代码中添加如下代码

b、在服务器端代码中 添加如下代码

二、开源框架实现发请求,并且解析(重要):
a、get方式

b、post方式

三、开源项目实现多线程下载(重点)
多线程下载的原理或步骤:
-
在本地创建一个服务器端一模一样大小的空文件:
大小:content-length;
RandomAccessFile setLength(); -
设置使用几个子线程去下载服务器上的文件:
在应用程序中设置变量代表子线程的个数; -
每个子线程下载的数据块的大小:
length/threaCount=blocksize; -
计算每个子线程下载开始位置和结束位置:
开始位置:threadId * blocksize; 结束位置:(threadId+1) * blocksize -1;
最后一个子线程下载结束位置: length-1; -
创建子线程,下载数据:
设置每个子线程下载数据的范围:Range:bytes=0-3 -
知道在什么时候文件下载完成,所有的子线程都下载完毕:
在程序中设置变量代表正在运行的线程的个数,当每个子线程下载完毕都去减1,当变量为0时表示所有的子线程都结束了;
-
代码:
package com.itheima.download;
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;
/*
* 多线程 下载器
*
*/
public class MultiThreadDownloader {
// http://188.188.5.100:8080/test.txt
static final String path ="http://188.188.5.100:8080/tt.exe";
static final int threadCount=3; //下载的线程的数量
static int runningThreadCount =3;
public static void main(String[] args) {
//1\. 连接 服务器, 在本地建一个同样 大小的空文件
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
//拿到 要下载的文件的长度
int length = conn.getContentLength();
//在本地建一个同样大小的空文件
RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rws");
raf.setLength(length); // 设置一个 空文件的大小
raf.close();
System.out.println(length);
//拿到 平均 每块 多大
int blockSize = length/threadCount;
//启动多条线程下载了
for (int threadId = 0; threadId < threadCount; threadId++) {
//开始索引
int startIndex = threadId*blockSize;
//结束索引
int endIndex = (threadId+1)*blockSize-1;
//如果是最后一条线程, 那么 结束索引的 位置 是 length-1
if(threadId==threadCount-1){
endIndex=length-1;
}
new Downloader(threadId, startIndex, endIndex).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getFileName(String path){
int index = path.lastIndexOf("/");
return path.substring(index+1);
}
private static class Downloader extends Thread{
private int threadId;
private int startIndex;
private int endIndex;
private int currentPosition; //默认的 实时位置 就是 开始 索引位置
public Downloader(int threadId, int startIndex, int endIndex){
this.threadId= threadId;
this.startIndex= startIndex;
this.endIndex= endIndex;
this.currentPosition =startIndex;
}
@Override
public void run() {
//干 下载目标段的数据的事儿
System.out.println("第 " + threadId +"号 线程, 下载 :" + startIndex+"~"+endIndex);
//连网络, 要 目标段 的数据
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
RandomAccessFile raf = new RandomAccessFile(getFileName(path), "rws");
//先去判断 记录了位置的文件是否存在, 如果存在,就 是断点 续传, 否则, 就从 开始 位置下载.
File fll = new File(threadId+".position");
if(fll.exists()&&fll.length()>0){
//说明有 记录, 那么就 需要读取这个文件的 值,然后从这个值所在的位置 继续下载
BufferedReader br = new BufferedReader(new FileReader(fll));
String vl = br.readLine();
currentPosition = Integer.parseInt(vl);
br.close();
//指定要 目标段的数据
conn.setRequestProperty("range", "bytes="+currentPosition+"-"+endIndex);
raf.seek(currentPosition);
}else{
//告诉 从空文件的目标位置开始写
raf.seek(startIndex);
//从 startIndex开始
conn.setRequestProperty("range", "bytes="+startIndex+"-"+endIndex);
}
//要目标段的数据 -- 如何找服务器要目标段 的数据 ?
// 通过http的请求 头 来实现 --打开 http 协议的文档
// - The first 500 bytes (byte offsets 0-499, inclusive): bytes=0-499
// range:bytes=0-499
// 如果访问 服务器, 服务器很好的处理了请求, 那么返回的状态码是 200 , 但是 注意, 这里
// 由于 只 找服务器 要 目标 段的数据(是整个文件的一部分的数据), 这个时候, 返回的状态码 不是 200 , 是 206 (Partial Content)
int code = conn.getResponseCode();
if(code==206){
System.out.println("====");
//获得服务器 给的那个 目标段的 流的数据, 然后 将这个目标段的数据写到 那个 空文件中去
InputStream in = conn.getInputStream();
int len=0;
byte[] buf= new byte[1024];
while((len=in.read(buf))>0){
raf.write(buf, 0, len);
currentPosition+=len;
//要把 currentPosition 给记录下 来 -- 写到 文件中去
File fl = new File(threadId+".position");
RandomAccessFile rf = new RandomAccessFile(fl, "rwd");
rf.write((currentPosition+"").getBytes());
rf.close();
}
raf.close();
}
//表示 那条 线程下载完成.
System.out.println(" 第 " + threadId +"号 线程下载结束 ");
//可以将 下载 过程中,生成的 用来保存 记录的文件给删除掉
File fl = new File(threadId+".position");
boolean b = fl.renameTo(new File(threadId+".position.finish"));
System.out.println(b+"+++++");
synchronized (MultiThreadDownloader.class) {
runningThreadCount--;
if(runningThreadCount<=0){
for (int threadId = 0; threadId < threadCount; threadId++) {
//将 每个 .position.finish 给删掉
File f = new File(threadId+".position.finish");
f.delete();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 引入xUtils-2.6.10.jar文件;
代码:
package com.itheima.xutils;
import java.io.File;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
public class MainActivity extends Activity {
private TextView tv_start;
private TextView tv_progress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_start = (TextView) findViewById(R.id.tv_start);
tv_progress = (TextView) findViewById(R.id.tv_progress);
}
public void download(View view){
HttpUtils http = new HttpUtils();
String path = "http://192.168.1.254:8080/11.exe";
http.download(path, Environment.getExternalStorageDirectory()+"/11.exe", true, new RequestCallBack<File>() {
@Override
public void onSuccess(ResponseInfo<File> response) {
Toast.makeText(MainActivity.this, "文件下载,保存在"+response.result.getPath(), 0).show();
}
@Override
public void onFailure(HttpException e, String str) {
Toast.makeText(MainActivity.this, "文件失败", 0).show();
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
super.onLoading(total, current, isUploading);
tv_progress.setText(current+"/"+total);
}
@Override
public void onStart() {
super.onStart();
tv_start.setText("开始下载...");
}
});
}
}
四、开源项目实现多线程上传(重点)
public class MainActivity extends Activity {
EditText ed_path;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ed_path = (EditText) findViewById(R.id.ed_path);
}
//实现文件的上传
public void upload(View v){
String path = ed_path.getText().toString().trim();
if(TextUtils.isEmpty(path)){
Toast.makeText(this, "对不起, 路径不能为空", 0).show();
return;
}
File file = new File(path);
if(!file.exists()||file.length()<=0){
Toast.makeText(this, "对不起, 不是有效的文件 ", 0).show();
return;
}
// 使用 utils实现文件的上传
RequestParams params = new RequestParams();
params.addBodyParameter("file", file);
HttpUtils utils = new HttpUtils();
// 上传的路径
String url = "http://188.188.5.100:8080/day11_upload/upload";
utils.send(HttpRequest.HttpMethod.POST, url, params,new RequestCallBack<String>(){
//成功
@Override
public void onSuccess(ResponseInfo responseInfo) {
Toast.makeText(MainActivity.this, "上传成功", 0).show();
System.out.println("====== 上传成功 ");
}
@Override
public void onStart() {
System.out.println("======开始 ");
super.onStart();
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
// TODO Auto-generated method stub
System.out.println("======正在进行中 ");
System.out.println("======正在进行中 ");
super.onLoading(total, current, isUploading);
}
//失败
@Override
public void onFailure(HttpException error, String msg) {
System.out.println("====== 上传 失败 ");
Toast.makeText(MainActivity.this, "上传失败", 0).show();
}});
}
}
网友评论