访问排行榜
谷歌广告
谷歌广告
断点续传下载速度限制
Author zero | Posted 2016-12-15 14:49:00

断点续传下载速度限制

今天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. 代码中一下错误处理都很简单,虽然我没有贴代码,但是明眼人一眼就可以看出来了。 下一章我来把静默下载的部分代码贴一下。

Android+GoLang+SprintBoot探讨群:186305789(疯狂的程序员),绝影大神在等你

个人兴趣网站:zero接码平台

个人兴趣网站:猿指


Comments

comments powered by zero