web大文件下载+断点续传
实现原理
(1)首先获得下载文件的长度,然后设置本地文件的长度。
(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。
如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示:
?例如10M大小,使用3个线程来下载,
线程下载的数据长度 ? (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M
下载开始位置:线程id*每条线程下载的数据长度 = ?
下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?
Activity代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@SuppressLint("HandlerLeak")
public static Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
int size = msg.getData().getInt("size");
// progressbar.setProgress();
float temp = (float) size / (float)progressbar.getMax();
int progress = (int) (temp * 100);
if (progress == 100) {
Log.e("TAG", "handleMessage: " + "下载完成");
}
tv_result.setText("下载进度:" + progress + "%");
}
};
private EditText ed;
private Button btn_download;
private static ProgressBar progressbar;
private static TextView tv_result;
private String urlstring;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
ed = (EditText) findViewById(R.id.ed);
btn_download = (Button) findViewById(R.id.btn_download);
progressbar = (ProgressBar) findViewById(R.id.progressbar);
tv_result = (TextView) findViewById(R.id.tv_result);
btn_download.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_download:
submit();
download();
break;
}
}
//下载文件
private void download() {
//获取sd卡路径
String path = Environment.getExternalStorageDirectory() + "/morethreaddownload/";
File file = new File(path);
if (!file.exists()) {
// 如果文件目录不存在,就创建此文件夹
file.mkdir();
}
// 指定下载的大文件的名称
String filename = "fulin.apk";
// 要下载的文件路径
String filepath = path + filename;
// 线程的数量
int threadcount = 5;
// 开启子线程下载文件(获取文件的长度)
DownLoadTask task = new DownLoadTask(progressbar,urlstring,filepath,threadcount);
task.start();
}
private void submit() {
//输入框中用户输入的网络路径
urlstring = ed.getText().toString().trim();
if (TextUtils.isEmpty(urlstring)) {
Toast.makeText(this, "edString不能为空", Toast.LENGTH_SHORT).show();
return;
}
}
}
DownLoadTask类
public class DownLoadTask extends Thread {
// 网路文件的下载地址
private String downloadUrl;
// 保存文件的路径
private String filePath;
// 线程的数量
private int threadcount;
// 进度条
private ProgressBar progressBar;
// 文件的大小
public static int filesize;
// 每个线程的下载量
private int blockSize;
//构造方法
public DownLoadTask(ProgressBar progressBar, String downloadUrl, String filePath, int threadcount) {
this.progressBar=progressBar;
this.downloadUrl = downloadUrl;
this.filePath = filePath;
this.threadcount = threadcount;
}
@Override
public void run() {
super.run();
// 线程数组
FileDownloadThread[] threads = new FileDownloadThread[threadcount];
try {
URL url = new URL(downloadUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
if (conn.getResponseCode() == 200) {
// 读取下载文件的总长度
filesize = conn.getContentLength();
if (filesize <= 0) {
Log.e(TAG, "run: " + "读取文件失败");
return;
}
// 设置progressbar的最大值
progressBar.setMax(filesize);
//
// 计算每条线程下载的数据长度
blockSize = filesize % threadcount == 0 ? filesize / threadcount : filesize / threadcount + 1;
//sd卡里边指定的保存网络端下载下来的文件
File file = new File(filePath);
// 启动每个线程,分别下载所分配的长度
for (int i = 0; i < threadcount; i++) {
threads[i] = new FileDownloadThread(file,url,(i+1),blockSize);
threads[i].setName("Thread:" + i);
threads[i].start();
}
//是否下载完成
boolean isfinished = false;
// 下载的总长度
int downloadAllsize = 0;
while (!isfinished) {
isfinished = true;
downloadAllsize = 0;
for (int i = 0; i < threadcount; i++) {
downloadAllsize += threads[i].getDownloadlenght();
if (!threads[i].isCompleted()) {
isfinished = false;
}
}
// 通知handler去更新
Message message = new Message();
message.getData().putInt("size", downloadAllsize);
MainActivity.handler.sendMessage(message);
Thread.sleep(1000);
}
Log.e(TAG, "run:下载的总大小: "+ downloadAllsize);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class FileDownloadThread extends Thread {
/**
* 文件保存路径
*/
private File file;
/**
* 文件下载路径 (文件网址)
*/
private URL downloadUrl;
/**
* 当前下载线程ID
*/
private int threadId;
/**
* 线程下载数据长度
*/
private int blockSize;
private int downloadlenght;
private boolean isCompleted;
public FileDownloadThread(File file, URL downloadUrl, int threadId, int blockSize) {
this.file = file;
this.downloadUrl = downloadUrl;
this.threadId = threadId;
this.blockSize = blockSize;
}
@Override
public void run() {
super.run();
BufferedInputStream bis=null;
RandomAccessFile raf=null;
try {
HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
conn.setAllowUserInteraction(true);
// 开始的位置
int startpos=blockSize*(threadId-1);
int endpos=blockSize*threadId-1;
// 设置当前线程下载的开始点和结束点
conn.setRequestProperty("Range","bytes="+startpos+"-"+endpos);
byte[] buffer=new byte[1024];
bis=new BufferedInputStream(conn.getInputStream());
raf=new RandomAccessFile(file,"rwd");
raf.seek(startpos);
int len=0;
while((len=bis.read(buffer,0,1024))!=-1){
raf.write(buffer,0,len);
downloadlenght+=len;
}
isCompleted=true;
} catch (IOException e) {
e.printStackTrace();
}
}
//每个线程下载的数据长度
public int getDownloadlenght() {
return downloadlenght;
}
//返回线程是否下载完成
public boolean isCompleted() {
return isCompleted;
}
}
FileDownloadThread类
class FileDownloadThread extends Thread {
/**
* 文件保存路径
*/
private File file;
/**
* 文件下载路径 (文件网址)
*/
private URL downloadUrl;
/**
* 当前下载线程ID
*/
private int threadId;
/**
* 线程下载数据长度
*/
private int blockSize;
private int downloadlenght;
private boolean isCompleted;
public FileDownloadThread(File file, URL downloadUrl, int threadId, int blockSize) {
this.file = file;
this.downloadUrl = downloadUrl;
this.threadId = threadId;
this.blockSize = blockSize;
}
@Override
public void run() {
super.run();
BufferedInputStream bis=null;
RandomAccessFile raf=null;
try {
HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
conn.setAllowUserInteraction(true);
// 开始的位置
int startpos=blockSize*(threadId-1);
int endpos=blockSize*threadId-1;
// 设置当前线程下载的开始点和结束点
conn.setRequestProperty("Range","bytes="+startpos+"-"+endpos);
byte[] buffer=new byte[1024];
bis=new BufferedInputStream(conn.getInputStream());
raf=new RandomAccessFile(file,"rwd");
raf.seek(startpos);
int len=0;
while((len=bis.read(buffer,0,1024))!=-1){
raf.write(buffer,0,len);
downloadlenght+=len;
}
isCompleted=true;
} catch (IOException e) {
e.printStackTrace();
}
}
//每个线程下载的数据长度
public int getDownloadlenght() {
return downloadlenght;
}
//返回线程是否下载完成
public boolean isCompleted() {
return isCompleted;
}
}
详细配置信息可以参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/28/net%e6%96%87%e4%bb%b6%e6%89%b9%e9%87%8f%e4%b8%8b%e8%bd%bd/
web大文件下载+断点续传的更多相关文章
- java web 大文件下载
泽优大文件下载产品测试 泽优大文件下载控件down2,基于php开发环境测试. 开发环境:HBuilder 服务器:wamp64 数据库:mysql 可视化数据库编辑工具:Navicat Premiu ...
- php大文件下载+断点续传
如果我们的网站提供文件下载的服务,那么通常我们都希望下载可以断点续传(Resumable Download),也就是说用户可以暂停下载,并在未来的某个时间从暂停处继续下载,而不必重新下载整个文件. 通 ...
- B/S大文件下载+断点续传
1.先将 webuploader-0.1.5.zip 这个文件下载下来:https://github.com/fex-team/webuploader/releases 根据个人的需求放置自己需要的 ...
- java大文件下载+断点续传
java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路:1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操 ...
- jsp大文件下载+断点续传
以多线程.断点续传方式下载文件,经常出现下载下来的文件大小和服务端一致,但是却无法正常打开的现象,搞了很久,贴下我的实现方式,请各位多多指教思路:1.将下载文件的处理放在自定义的线程类中,每下载一个文 ...
- Web大文件下载控件(down2)-示例更新-Xproer.HttpDownloader
版权所有 2009-2016 荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/down2/i ...
- Web大文件下载控件更新-Xproer.HttpDownloader
资源下载:cab安装包(x86),cab安装包(x64),xpi安装包,crx安装包,nat安装包,exe安装包,开发文档,根证书,VC库, 更新时间:2016-08-19 版本号:1,2,56, ...
- web大文件断点续传
1,项目调研 因为需要研究下断点上传的问题.找了很久终于找到一个比较好的项目. 在GoogleCode上面,代码弄下来超级不方便,还是配置hosts才好,把代码重新上传到了github上面. http ...
- Web大文件(夹)上传(断点续传)控件-Xproer.HttpUploader6
版权所有 2009-2017荆门泽优软件有限公司 保留所有权利 官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/up6.2/in ...
随机推荐
- FLUME安装&环境(二):拉取MySQL数据库数据到Kafka
Flume安装成功,环境变量配置成功后,开始进行agent配置文件设置. 1.agent配置文件(mysql+flume+Kafka) #利用Flume将MySQL表数据准实时抽取到Kafka a1. ...
- 【EWM系列】SAP EWM中仓库任务WT创建的函数
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[EWM系列]SAP EWM中仓库任务WT创建的 ...
- Vijos lxhgww的奇思妙想--求K级祖先
给出一棵树求K级祖先.O(N*logN+Q) 更详细的讲解见:https://www.cnblogs.com/cjyyb/p/9479258.html /* 要求k级祖先,我们可以把k拆成" ...
- xmake新增对WDK驱动编译环境支持
xmake v2.2.1新版本现已支持WDK驱动编译环境,我们可以直接在系统原生cmd终端下,执行xmake进行驱动编译,甚至配合vscode, sublime text, IDEA等编辑器+xmak ...
- oracle表名中带@什么意思
例如:select * from dim.dim_area_no@to_dw @后是实例名或数据源,一个简单例子,服务器上创建了2个数据库实例,名称分别为HR.BOSS, 如果你用PL/SQL DEV ...
- Redis的消息订阅及发布及事务机制
Redis的消息订阅及发布及事务机制 订阅发布 SUBSCRIBE PUBLISH 订阅消息队列及发布消息. # 首先要打开redis-cli shell窗口 一个用于消息发布 一个用于消息订阅 # ...
- 【Linux 环境搭建】安装arm-linux-gcc
(1)下载或者拷贝arm-linux-gcc-4.3.2.tgz到Linux环境的任意目录. 解压: tar xvf arm-linux-gcc-4.3.2.tgz ,会解压该文件到当前目录: 拷贝: ...
- 洛谷P2507 [SCOI2008]配对 题解(dp+贪心)
洛谷P2507 [SCOI2008]配对 题解(dp+贪心) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1299251 链接题目地址:洛谷P2507 [S ...
- Python 入门之 推导式
Python 入门之 推导式 推导式就是构建比较有规律的列表,生成器,字典等一种简便的方式 1.推导式 (1)列表推导式 : <1> 普通循环: [变量 for循环] print([i f ...
- Java的同名属性、同名普通函数、同名静态函数,是否被覆盖
作者按:虚拟函数的概念早就滚瓜烂熟了.但是今天面试发现:1.同名属性,2.同名普通函数,3.同名静态函数,是否被覆盖的问题.请看下面三个例子: 例子1:测试父类的属性是否存在和被完全覆盖class A ...