IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> android实现短线续传功能 -> 正文阅读

[网络协议]android实现短线续传功能

实现断点就会需要用到服务器,关于服务器可以使用tomcat 、wampservser等,在该服务器上放置一个体积足够大的文件,不然局域网超快的速度下载和传输,你都没有反应过来什么事。
网络传输就会涉及到权限问题,android版本的不断更新,,对于用户的权限那可是十分的重要和棘手,自从android某版本开始以后,安卓上开发传输已经不在建议使用http协议传输,官方的理由就是明文传输不安全,建议使用https协议传输。相对于http协议,https传输会更加的安全。

HTTP协议一般指HTTP
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII形式给出;而消息内容可以明文发送。

HTTPS协议
(全称:Hyper Text Transfer Protocol over SecureSocket Layer),是以安全为目标的 HTTP 通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性。HTTPS 在HTTP 的基础下加入SSL,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。
备注:SSL是一种域名验证的加载的证书,验证身份信息的加密的证书。

下载文件[思路]

  1. 需要服务器以及可以提供下载的地址
  2. 通过URL对象获取连接,以及请求方式
  3. 官方为了更好的体验,必须使用子线程开启下载操作,主线程操作下载操作直接出错
  4. 下载文件,开启线程,需要确定下载文件的子线程的数量 每个子线程需要下载模块的大小 记录当前的线程运行的数量 文件的存储目录
  5. android需要接入互联网,要在本地存储操作文件,需要在mainfest文件申请权限
  6. 需要动态申请权限[官方规定]
  7. 创建子线程类,需要确定子线程id 开始的位置 结束的位置 下载文件的path
  8. 断点下载需要向服务器发送Range 请求头 例如: conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);//告诉浏览器分段下载
  9. 需要使用RandomAccessFile操控文件读取指针的位置,
  10. 确定每一个子线程都能完成下载的模块的byte 才可以删除临时记录的文件
  11. 在数据中0也是数据,如果是最后一个字节必须减1个byte
  12. 在finlly中操作删除临时记录文件,一定要注意线程的脏数据以及线程并发操作,必须要使用加锁方式处理

具体代码实现如下:

关于mainfest文件申请权限可以查阅别人的代码,网上应该有!

package com.example.a02downloadfile;
/*
功能:实现多线程下载 实现断点下载
注意:一定要要动态获取存储权限,这里是手动开启权限
 */
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;


public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE };
    private static int threadCount = 3; //设置线程的数量为3
    private static long blockSize; //声明每个下载区块的大小
    private static int runninbgThreadCount; //声明正在运行的线程数量
    private static File extDir = Environment.getExternalStorageDirectory(); //设备存储目录
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        int permission = ActivityCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE);
        }



        //192.168.2.15
        //http://192.168.2.15/school/temp.exe
        String path = "http://192.168.2.15/school/temp.exe"; //服务器下载文件的路径

        new Thread(new Runnable() {
            @Override
            public void run() {
                //实例化url对象
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //获取连接对象
                    conn.setRequestMethod("GET");//设置请求方式为get请求
                    conn.setConnectTimeout(5000); //设置超时5s
                    int code = conn.getResponseCode(); //获取返回的请求码
                    if(code ==200){ //如果返回码等于200,代表请求返回成功
                        long size = conn.getContentLength(); //获取返回数据长度
                        System.out.println("获取服务器文件大小:"+size+"KB");
                        blockSize = size/threadCount; //分发每一个下载模块的大小
                        File file = new File(extDir,"temp.exe"); //在本地创建一个与服务器同样的空白文件
                        System.out.println(file);
                        RandomAccessFile raf = new RandomAccessFile(file,"rw"); //随机读写文件
                        raf.setLength(size);
                        runninbgThreadCount = threadCount;//开启若干个子线程分别去下载对应的资源
                        for(int i=1; i<= runninbgThreadCount; i++){
                            long startIndex = (i-1)*blockSize; //开始位置 0
                            long endIndex = i*blockSize - 1; //结束位置 2
                            if(i==threadCount){ //如果是最后一个线程
                                endIndex = size - 1; // 0也算一个数据。因此也要减去该位
                            }
                            System.out.println("开启线程:"+i+" 下载位置:"+startIndex+"结束位置:"+endIndex+"~");
                            //url路径 线程id 开始下载位置 结束下载位置
                            new DownloadThread(path,i,startIndex,endIndex).start();
                        }
                        conn.disconnect();
                        raf.close();
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    //创建子线程
    private  static class DownloadThread extends Thread{
        private int threadID; //子线程ID
        private long startIndex; //下载开始位置
        private long endIndex; //下载结束位置
        private String path; //文件路径
        public DownloadThread(String path, int threadID, long startIndex, long endIndex){
            this.path = path;
            this.threadID = threadID;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        @Override
        public void run() {
            try {
                int total = 0; //当前总下载的大小
                File postionFile = new File(extDir,threadID+".txt"); //线程文件.txt
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                //接着上一次下载的数据
                if(postionFile.exists() && postionFile.length()>0){ //如果该文件存在并且大于0 代表有数据
                    FileInputStream fis = new FileInputStream(postionFile); //读取该文件
                    BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //reader对象
                    String lastTotalstr = br.readLine(); //A获取当前线程上一次下载的总大小
                    int lastTotal = Integer.valueOf(lastTotalstr);
                    System.out.println("上一次线程"+threadID+"下载了总大小:"+lastTotal+"KB");
                    startIndex += lastTotal; //累加上一次下载的总大小
                    total += lastTotal; //当前累加下载的总大小
                    br.close();
                    fis.close();
                }
                conn.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);//告诉浏览器分段下载
                conn.setConnectTimeout(5000);
                int code = conn.getResponseCode();
                System.out.println("code = "+code);//服务器响应206 代表url可以访问任何资源
                InputStream is = conn.getInputStream();//获取服务器流
                File file = new File(extDir,"temp.exe");
                RandomAccessFile raf = new RandomAccessFile(file,"rw");
                raf.seek(startIndex); //指定文件开始写的位置
                System.out.println("第"+threadID+"个线程:写文件开始的位置"+String.valueOf(startIndex));
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = is.read(buffer))!= -1){
                    raf.write(buffer,0,len);
                    RandomAccessFile rf = new RandomAccessFile(postionFile,"rwd");
                    total += len;
                    rf.write(String.valueOf(total).getBytes()); //转换为字节数
                    rf.close();
                }
                raf.close();
                is.close();
                conn.disconnect();
                System.out.println("线程:"+threadID+"下载完毕");
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                //只有所有的线程下载玩文件之后,才可以删除文件记录
                synchronized (DownloadThread.class){
                    System.out.println("线程"+threadID+"下载完毕了,可以删除文件了");
                    runninbgThreadCount--; //每当下载完一个任务就相应减去一个线程个数
                    if(runninbgThreadCount<1) { //代表当前没有线程
                        System.out.println("所有的线程都完成了工作,开始删除临时记录文件了");
                        for (int i = 1; i <= threadCount; i++) {
                            File f = new File(extDir,i + ".txt");
                            System.out.println(f);
                            System.out.println(f.delete());
                        }
                    }
                }
            }

        }
    }
}

操作测试时,一定要即使删除下载的临时文件,这里没判断读取累加的数据与原数据比较解析,会出现下载文件打不开或者数据损毁等。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-08 08:27:37  更:2022-05-08 08:30:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 8:17:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码