Android 断点续传,简单易用,快速实现断点续传功能,Android文件下载、安卓文件下载、安卓断点续传、阿里云OSS断点续传、AliyunOSS、Apk更新、Weex更新、Android动态更新

    技术2022-07-11  81

    Android断点续传文件下载,简单实用的代码,如需要可参考。

    希望不要觉得我又造一个轮子,我先用了网上代码觉得不好使用,而且发现有错误;

    有一些功能强大的框架,我懒得去了解,去学习怎样使用它也要花时间;

    简单实现自己的功能就好,我比较倾向于容易理解,简单实用。

     

    本人老程序员一个,没有专注和擅长哪一个开发环境;

    最近要开发一个自助售卖机的客户端,

    今天要实现文件下载功能,用于更新设备上的广告(mp4视频文件),

    还有用于版本升级,下载APK文件和Weex打包的文件;

    设备都是用4G物联网卡,流量不多,所以要有断点续传。

     

    下面是写好的下载类:

     

    import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import okhttp3.Headers; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class WGDownloadManager { /** * 下载文件 * @param downloadUrl * @param savePath * @param saveName * @param callback */ public void downloadFile(final String downloadUrl, final String savePath, final String saveName, final IDownloadFile callback) { checkDownloadFile(downloadUrl, new ICheckDownloadFile() { @Override public void onResult(long contentLength, boolean isAcceptRanges) { /** * 1. 下载的时候,先保存为saveName+“.tmp”结尾的临时文件 * 2. 下载完成后,再把临时文件重命名为正式文件名(saveName) */ if (callback != null) { callback.onBegin(contentLength, isAcceptRanges); } try { // 检查保存目录,不存在就创建 File fPath = new File(savePath); if (!fPath.exists()) { fPath.mkdirs(); } // 检查文件是否已下载,已下载成功就直接返回 File fFile = new File(fPath, saveName); if (fFile.exists() && fFile.length() == contentLength) { if (callback != null) { callback.onFinish(0); } return; } // 检查临时文件,已下载完成的,就改名并直接返回 File tmpFile = new File(fPath, saveName+".tmp"); long downLoadLen = tmpFile.exists() ? tmpFile.length() : 0; if (tmpFile.exists() && downLoadLen == contentLength) { if (fFile.exists()) { fFile.delete(); } tmpFile.renameTo(fFile); if (callback != null) { callback.onFinish(0); } return; } // 判断是否支持断点续传,不支持就删除临时文件 if (!isAcceptRanges && tmpFile.exists()) { downLoadLen = 0; tmpFile.delete(); } // 打开临时文件 RandomAccessFile rndTmpFile = new RandomAccessFile(tmpFile, "rwd"); rndTmpFile.seek(downLoadLen); // 下载 OkHttpClient client = new OkHttpClient(); //String rangeValue = "bytes="+downLoadLen+"-"+(contentLength-1);//注意:contentLength-1(网上的某代码有错) String rangeValue = "bytes="+downLoadLen+"-"; Request request = new Request.Builder().url(downloadUrl).addHeader("Range", rangeValue).build(); Response response = client.newCall(request).execute(); InputStream input = response.body().byteStream(); int readLen; long readLenCount = 0; boolean isFinish = false; byte[] bytes = new byte[5120]; while (!isFinish && (readLen = input.read(bytes)) != -1) { rndTmpFile.write(bytes, 0, readLen); downLoadLen += readLen; readLenCount += readLen; double pro = downLoadLen / (contentLength * 1.0); int percent = (int)(pro * 100); if (downLoadLen >= contentLength) { isFinish = true; } if (callback != null) { callback.onProgress(percent, downLoadLen, contentLength); } } rndTmpFile.close(); response.close(); if (isFinish) { if (fFile.exists()) { fFile.delete(); } tmpFile.renameTo(fFile); if (callback!=null) { callback.onFinish(readLenCount); } } } catch (Exception e) { if (callback != null) { callback.onError(e.getLocalizedMessage()); } } } @Override public void onError(String errMsg) { if (callback != null) { callback.onError(errMsg); } } }); } /** * 检测下载文件(获取文件大小和判断是否支持断点续传) * @param downloadUrl * @param callback */ private void checkDownloadFile(final String downloadUrl, final ICheckDownloadFile callback) { new Thread(new Runnable() { @Override public void run() { try { // 发送带有头字段“Range: bytes=0-0”的请求,以判断是否支持断点续传 OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(downloadUrl).addHeader("Range", "bytes=0-0").build(); Response response = client.newCall(request).execute(); long length = 0; // response.body().contentLength();//带了头字段“Range”,这样取不准确 Headers headers = response.headers(); boolean isAcceptRanges = false; for (int i = 0; i < headers.size(); i++){ String headerName = headers.name(i); if (headerName.equalsIgnoreCase("Content-Range")) { isAcceptRanges = true; String headerValue = "" + headers.get(headerName); //值示例:bytes 0-1/6764378 String strNum = headerValue.indexOf("/") > 0 ? headerValue.substring(headerValue.indexOf("/")+1) : "0"; length = Long.parseLong(strNum); break; } } response.close(); // 如果不支持断点续传,发送一个普通请求,取得contentLength if (!isAcceptRanges) { OkHttpClient client2 = new OkHttpClient(); Request request2 = new Request.Builder().url(downloadUrl).build(); Response response2 = client2.newCall(request2).execute(); length = response2.body().contentLength(); response.close(); } if (callback != null) { callback.onResult(length, isAcceptRanges); } } catch (Exception e) { if (callback != null) { callback.onError(e.getLocalizedMessage()); } } } }).start(); } /** * 下载回调 */ public interface IDownloadFile { void onBegin(long contentLength, boolean isAcceptRanges); void onProgress(int percent, long downloadSize, long totalSize); void onFinish(long downloadSize); void onError(String errMsg); } /** * 检查文件回调 */ private interface ICheckDownloadFile { void onResult(long contentLength, boolean isAcceptRanges); void onError(String errMsg); } }

     

    下面是使用方法参考:

     

     

    String downloadUrl = "https://cloud.video.taobao.com/play/u/2780279213/p/1/e/6/t/1/d/ld/36255062.mp4"; String savePath = MyFileUtils.getSDCardPath("wongo"); String saveName = MyFileUtils.generateFileName(downloadUrl); WGDownloadManager downloadManage = new WGDownloadManager(); downloadManage.downloadFile(downloadUrl, savePath, saveName, new WGDownloadManager.IDownloadFile() { @Override public void onBegin(long contentLength, boolean isAcceptRanges) { App.app().myLog("下载开始,文件大小:" + contentLength + ",是否支持断点续传:" + isAcceptRanges); } @Override public void onProgress(int percent, long downloadSize, long totalSize) { App.app().myLog("下载进度:"+percent+"%,已下载:"+downloadSize+",总大小:"+totalSize); } @Override public void onFinish(long downloadSize) { App.app().myLog("下载完成,本次下载大小:" + downloadSize); } @Override public void onError(String errMsg) { App.app().myLog("下载出错:" + errMsg); } });

     

    额外说明一点,我看到网上好几份代码没有把这地方写对,如下:

    // 下载 OkHttpClient client = new OkHttpClient(); //String rangeValue = "bytes="+downLoadLen+"-"+(contentLength-1);//注意:contentLength-1(网上的某代码有错) String rangeValue = "bytes="+downLoadLen+"-"; Request request = new Request.Builder().url(downloadUrl).addHeader("Range", rangeValue).build();

    我在好多遍的测试中,才找出这个隐藏错误。

     

    其它

    依赖okhttp包,请自行导入,例如:

    implementation 'com.squareup.okhttp3:mockwebserver:4.5.0'

    还有权限申明,如:

    <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

     

     

    唠叨一下,Weex (https://weex.apache.org/)目前坑很多,本以为这么多年它比较完善了,结果坑得够惨。

     

     

    简单漏一下我的机器照:

     

     

    --------(完)--------

     

    Processed: 0.010, SQL: 9