Android okhttp多线程上传下载文件(封装成sdk) 支持断点 分片下载 文件加密

1.多线程分片上传原理

2.断点上传和下载的原理

3.取消上传的原理

4.文件上传下载加密

5.文件合法性和完整性验证

 

 

文件下载的流程:

1.得到文件的大小,名字,md5

2.分块,验证每一块的md5

3.断线下载数据库记录

4.合法验证

 

1. 断点续传原理

在本地下载过程中要使用数据库实时存储到底存储到文件的哪个位置了,这样点击开始继续传递时,才能通过HTTP的GET请求中的setRequestProperty()方法可以告诉服务器,数据从哪里开始,到哪里结束。同时在本地的文件写入时,RandomAccessFile的seek()方法也支持在文件中的任意位置进行写入操作。同时通过广播将子线程的进度告诉Activity的ProcessBar。

--------------------- 

使用多线程的好处:使用多线程下载会提升文件下载的速度。那么多线程下载文件的过程是: 

  (1)首先获得下载文件的长度,然后设置本地文件的长度。

      HttpURLConnection.getContentLength();//获取下载文件的长度

     RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");

       file.setLength(filesize);//设置本地文件的长度

 

  (2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。

      如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。

  

   例如10M大小,使用3个线程来下载,

        线程下载的数据长度   (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M

         下载开始位置:线程id*每条线程下载的数据长度 = ?

        下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?

 

  (3)使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止,

         如:指定从文件的2M位置开始下载,下载到位置(4M-1byte)为止

           代码如下:HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");

 

  (4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。

RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");

threadfile.seek(2097152);//从文件的什么位置开始写入数据

 

 

多线程结合断点续传下载

多线程无非是将待下载的文件分成若干个部分进行下载并实现断点续传。

1.同样,我们首先要获取待下载的文件的长度,用来为每个线程分配下载长度。通过HttpURLConnection.getContentLength()获取待下载的文件的长度。如下:
filesize=connection.getContentLength();

2.通过前面获取的下载文件的长度,为每个线程计算下载长度,即为每个线程设置下载的起始位置跟结束位置。通过HttpUrlConnection.setRequestProperty("Range", "bytes=" + 开始位置+ "-" + "结束位置")方法。如下图所示:
[图片上传失败...(image-f5634c-1521193484969)]

计算方法如下:
int block = (filesize % threadCount == 0) ? filesize / threadCount : filesize / threadCount + 1;
所以每个线程对应的起始位置跟结束位置分别为:**i block, (i + 1) block(i从0开始)

3.通过RandomAccessFile可以在文件指定位置写入数据。如下:
mRandomAccessFile.seek(startPos)

4.为每个线程的下载的进度都保存数据,这样每次每次暂停后重新下载都重新读取下载进度,并且可以从上次位置重新下载。并且再本地文件中继续读入数据。

 

在MyThread中,通过info.getStart() + info.getNow()设置开始下载的位置,如果是第一次下载两个数将都是0,如果是暂停后再下载,则info.getNow()会取出非0值,该值来自数据库存储。使用setRequestProperty告知服务器从哪里开始传递数据,传递到哪里结束,本地使用RandomAccessFile的seek()方法进行数据的本地存储。使用广播将进度的百分比传递给Activity,Activity再改变ProcessBar进行UI调整。

 

这里很关键的一点是在用户点击暂停后会在Service中调用downLoadUtil.isPause = true,因此上面while循环会结束,停止下载并通过数据库的update()保存进度值。从而在续传时取出该值,重新对服务器发起文件起始点的下载任务请求,同时也在本地文件的相应位置继续写入操作。本文原创,转载请注明出处为SEU_Calvin的博客。

 

最后补充一点,(17/4/11被面试问到了)关于断线续传的HTTP状态码并不是200,而是206,即HttpStatus.SC_PARTIAL_CONTENT。

--------------------- 

代码如下:

@Override

@Override

public void run() {

    HttpURLConnection conn = null;

    RandomAccessFile raf = null;

    try {

        //连接网络文件

        URL url = new URL(mFileInfo.getUrl());

        conn = (HttpURLConnection) url.openConnection();

        conn.setConnectTimeout(3000);

        conn.setRequestMethod("GET");

        int length = -1;

        if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {

            //获得文件长度

            length = conn.getContentLength();

        }

        if (length <= 0) {

            return;

        }

        //在本地创建文件

        File dir = new File(DOWNLOAD_PATH);//验证下载地址

        if (!dir.exists()) {

            dir.mkdir();

        }

        File file = new File(dir, mFileInfo.getFileName());

        raf = new RandomAccessFile(file, "rwd");//r:读权限,w:写权限,d:删除权限

        //设置文件长度

        raf.setLength(length);

        mFileInfo.setLength(length);

        mHandler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();

    } catch (Exception e) {

        e.printStackTrace();

    } finally {

        conn.disconnect();

        try {

            raf.close();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

 

 

问题:

1.断点上传需要客户端支持吗?

2.知道下载链接,就可以下载,安全问题!

 

切片上传

https://blog.csdn.net/u011368551/article/details/81531880

 

demo地址:

https://blog.csdn.net/seu_calvin/article/details/53749776

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的。使用Okhttp进行文件断点下载,可以实现网络不稳定情况下,下载任务可以从上一次中断的位置继续下载,避免重复下载浪费网络资源和时间。下面是一个简单实现的代码示例: ```java public class FileDownloader { private OkHttpClient client; private Call call; private String url; private String saveDir; private String fileName; private long startPosition; private long currentPosition; private boolean isDownloading = false; public FileDownloader(String url, String saveDir, String fileName, long startPosition) { this.url = url; this.saveDir = saveDir; this.fileName = fileName; this.startPosition = startPosition; this.currentPosition = startPosition; this.client = new OkHttpClient.Builder().build(); } public void startDownload() { isDownloading = true; Request request = new Request.Builder() .url(url) .addHeader("Range", "bytes=" + currentPosition + "-") .build(); call = client.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { isDownloading = false; e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { RandomAccessFile randomAccessFile = null; InputStream is = null; try { //获取文件长度 long contentLength = response.body().contentLength(); //设置文件存储路径 File saveFile = new File(saveDir, fileName); //判断文件是否存在,不存在则创建 if (!saveFile.exists()) { saveFile.createNewFile(); } //获取随机读写文件权限 randomAccessFile = new RandomAccessFile(saveFile, "rw"); //设置文件长度 randomAccessFile.setLength(contentLength); //设置文件写入的起始位置 randomAccessFile.seek(currentPosition); //获取输入流 is = response.body().byteStream(); byte[] buffer = new byte[2048]; int len = 0; //开始读取网络数据,并写入本地文件 while ((len = is.read(buffer)) != -1) { randomAccessFile.write(buffer, 0, len); currentPosition += len; } //关闭输入输出流 response.close(); is.close(); randomAccessFile.close(); } catch (Exception e) { e.printStackTrace(); } finally { if (randomAccessFile != null) { randomAccessFile.close(); } if (is != null) { is.close(); } isDownloading = false; } } }); } public void stopDownload() { if (isDownloading && call != null) { isDownloading = false; call.cancel(); } } public boolean isDownloading() { return isDownloading; } public long getCurrentPosition() { return currentPosition; } } ``` 这个封装的类可以在Android应用中使用。使用方法如下: ```java String url = "http://www.example.com/yourfile.mp4"; String saveDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); String fileName = "yourfile.mp4"; long startPosition = 0; FileDownloader downloader = new FileDownloader(url, saveDir, fileName, startPosition); downloader.startDownload(); ``` 其中,url为资源文件下载链接,saveDir为文件保存的目录,fileName为文件名,startPosition为起始下载位置。使用`downloader.stopDownload()`方法可以停止下载任务。使用`downloader.getCurrentPosition()`方法可以获取当前下载位置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值