断点续传下载速度限制
今天boss提出部分文件需要后台低速下载的要求,主要为了不影响正常使用,工作三四年还真没接触过限速下载这个功能,因此特地去看了一下资料。 和我自己所料不差,大部分限速都是在read之后通过Thread的sleep方法实现,即控制下载的吞吐量,这样可以达到一秒内下载量的限制,但是read的过程却无法做限制,这是一大遗憾,作者暂时还在资料搜寻中,先把这种简单的限速放给大家。
public class SpeedLimitUtil { private long prevTime = 0; private long bytePrev = 0;//前一次记录的文件大小 private long byteSum = 0;//总共读取的文件大小 private int fileLength = 0; private int limit = 0; public SpeedLimitUtil(long startSize,int fileLength,int limit){ this.prevTime = System.currentTimeMillis(); this.bytePrev = startSize; this.byteSum = startSize; this.fileLength = fileLength; this.limit = limit; } /** * 检查当前buffer下载的速度是否过快,过快则返回需要delay的时间 * @param len * @return */ public long checkSpeed(int len){ byteSum += len; //当前时间 long currentTime = System.currentTimeMillis(); int speed = 0; if (currentTime - prevTime > 0) { //避免两次读数太近,导致分母为0 speed = (int) ((byteSum - bytePrev) / (currentTime - prevTime)); } // LogUtil.e("HongLi","len:" + len + ";speed:" + speed + ";b length:" + b.length); if (limit > 0 && (fileLength - byteSum) > limit) {//设置了限速,且不是最后一次read if (speed > limit) {//如果本身速度小于limit就不需要限速 //计算出需要等待多久才能达到限速要求: int sleepTime = (int) ((byteSum - bytePrev) / limit + prevTime - currentTime); // LogUtil.e("HongLi","限速:" + "sleep " + sleepTime + "ms;byteRead:" + + len + ";speed:" + speed); return sleepTime > 0 ? sleepTime : 0; } } return 0; } /** * 检查并执行限速 * @param len * @throws InterruptedException */ public void limitSpeed(int len) throws InterruptedException{ long sleepTime = checkSpeed(len); if(sleepTime > 0){ Thread.sleep(sleepTime); } } }
SpeedLimitUtil用于限速,开始read循环之前创建实例,每次read执行一次limitSpeed方法,判断是否需要delay,delay多少ms。
/** * 断点续传下载文件 * * @param fileUrl * @param localFilePath * @return */ public static boolean downloadFile(String fileUrl, String localFilePath) { if (TextUtils.isEmpty(fileUrl) || TextUtils.isEmpty(localFilePath)) { return false; } File file = new File(localFilePath); long size = 0; if (file.exists()) { size = file.length(); } URL url; boolean downloadSuccess = false; RandomAccessFile out = null; HttpURLConnection con = null; int fileLength = 0; try { url = new URL(fileUrl); con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); con.setConnectTimeout(4000); con.setReadTimeout(10000); // 设置下载区间 con.setRequestProperty("range", "bytes=" + size + "-"); fileLength = con.getContentLength(); con.connect(); int code = con.getResponseCode();// 只要断点下载,返回的已经不是200,206 //LogUtil.e("HongLi","code:" + code); if (code == 206) { InputStream in = con.getInputStream(); // int serverSize = con.getContentLength(); // 必须要使用 out = new RandomAccessFile(file, "rw"); out.seek(size); byte[] b = new byte[2048]; int len = -1; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } out.close(); downloadSuccess = true; }else{ downloadSuccess = downloadFileNormal(fileUrl, localFilePath); } con.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); downloadSuccess = downloadFileNormal(fileUrl, localFilePath); } catch (IOException e) { e.printStackTrace(); downloadSuccess = downloadFileNormal(fileUrl, localFilePath); } catch (Exception e) { e.printStackTrace(); downloadSuccess = downloadFileNormal(fileUrl, localFilePath); } finally { try { if (null != out) { out.close(); } } catch (IOException e1) { e1.printStackTrace(); } if (null != con) { con.disconnect(); } if (!downloadSuccess && file.exists()) { file.delete(); } if(downloadSuccess && !checkDownloadFile(fileLength,file,fileUrl)){ file.delete(); downloadSuccess = downloadFile(fileUrl,localFilePath); } } return downloadSuccess; }
以上便是断点续传+限速的代码了,断点续传简单描述一下,其实要实现断点续传真的很简单,先看本地是否有下载到一半的文件,如果有则读取此文件的大小,HttpURLConnection请求之前把文件大小给服务器,服务器会从此大小之后返回数据,注意断点续传的responseCode为206. 代码中一下错误处理都很简单,虽然我没有贴代码,但是明眼人一眼就可以看出来了。 下一章我来把静默下载的部分代码贴一下。
Comments
comments powered by zero