【面试题目】Java文件下载(限速、进度条)

一、题目

请你实现一个程序,能够下载一个网络上的文件(例如:http://网络地址/文件.exe),并且要求间隔一定时间打印进度、当前下载速度和预计下载结束时间。格式如下:

50%[=========>                    ]1234.5K/s, will be finished in 23.04s

附加题:实现限速下载(即下载速度不会超过程序指定的速度)。



二、效果图

这里写图片描述

这里写图片描述

这里写图片描述

三、代码

零警告代码,放心食用。(环境:IntelliJ IDEA 2016.2、JDK1.8)

import javax.swing.*;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;

/**
 * 带进度条的限速的java文件下载
 *
 * @author Relish
 *         Created by Relish on 2016/7/14.
 */
public class ProgressBarDownLoader extends JFrame implements Runnable {
    //限速标志(单位:Kb/s)
    private static final double limitSpeed = 100;
    //下载文件地址
    private static final String URL = "http://113.215.21.37/1Q2W3E4R5T6Y7U8I9O0P1Z2X3C4V5B/mir.wandoujia.com/files/release2/WanDouJia_2.80.1.7144_homepage.exe";
    //存放地址(及拓展名)
    private static final String PATH = "D:/wdj.exe";

    private final ArrayList<Integer> proList;
    private int progress;//当前进度
    private int totalSize;//总大小
    private boolean run = true;
    private long prevTime = 0;
    private int prevSize = 0;
    private MyPanel panel;
    private long totalTime = 0;

    private ProgressBarDownLoader(int totalSize) {
        proList = new ArrayList<>();
        this.totalSize = totalSize;
        panel = new MyPanel();
        add(panel);
        setSize(500, 200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLayout(null);
        setResizable(false);
        setVisible(true);
    }

    /**
     * @param progress 进度
     */
    private void updateProgress(int progress) {
        synchronized (this.proList) {
            if (this.run) {
                this.proList.add(progress);
                this.proList.notify();
            }
        }
    }

    private void finish(long totalTime) {
        this.run = false;
        this.totalTime = totalTime;
        //关闭进度条
    }


    @Override
    public void run() {
        synchronized (this.proList) {
            try {
                while (this.run) {
                    if (this.proList.size() == 0) {
                        this.proList.wait();
                    }
                    synchronized (proList) {
                        this.progress += this.proList.remove(0);
                        long currentTime = System.currentTimeMillis();
                        boolean print = currentTime - prevTime >= 1000;
                        if (!print) continue;
                        int percent = (int) ((((double) (this.progress)) / ((double) (totalSize))) * 100);

                        //字符串拼接 start
                        StringBuilder sb = new StringBuilder();
                        sb.append(percent);
                        sb.append("%[");
                        for (int i = 1; i <= percent / 4; i++) {
                            sb.append("=");
                        }
                        if (percent / 4 >= 1)
                            sb.append(">");
                        for (int i = 0; i <= 25 - percent / 4; i++) {
                            sb.append("  ");//在JPanel上显示时(2个空格的大小=一个等号的大小)
                        }
                        sb.append("]");
                        double speed = ((double) (progress - prevSize)) / ((double) (currentTime - prevTime)) * 1000 / 1024;
                        sb.append(String.format("%.1f", speed));
                        sb.append("K/s, will be finished in ");
                        double leaveTime = ((double) (totalSize - progress)) / 1024 / speed;
                        sb.append(String.format("%.2f", leaveTime));
                        sb.append("s");
                        //字符串拼接end

                        panel.setText(sb.toString());
                        //在控制台上显示时(1个空格的大小=一个等号的大小)
                        System.out.println(sb.toString().replace("  ", " "));

                        prevSize = progress;
                        prevTime = currentTime;
                    }
                }
                panel.setText("100%, 下载完成, 共耗时" + (totalTime / 1000) + "s, 总大小为" + (totalSize / 1024) + "Kb.");
                System.err.println("100%, 下载完成, 共耗时" + (totalTime / 1000) + "s, 总大小为" + (totalSize / 1024) + "Kb.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static class MyPanel extends JPanel {

        private JLabel labelProgress;
        private JLabel jtfLimit;
        static double limit = limitSpeed;//限速标志

        MyPanel() {
            labelProgress = new JLabel();
            labelProgress.setSize(500, 100);
            labelProgress.setLocation(50, 50);
            add(labelProgress);

            jtfLimit = new JLabel("限速:" + limit + "K/s");
            jtfLimit.setSize(300, 100);
            jtfLimit.setLocation(50, 0);
            add(jtfLimit);

            setLayout(null);
            setSize(500, 200);
            setVisible(true);
        }


        void setText(String text) {
            labelProgress.setText(text);//更新显示进度
        }
    }


    public static void main(String[] args) throws MalformedURLException {
        // 下载网络文件
        try {
            URL url = new URL(URL);

            URLConnection conn = url.openConnection();
            InputStream inStream = conn.getInputStream();
            FileOutputStream fs = new FileOutputStream(PATH);
            int connLength = conn.getContentLength();
            ProgressBarDownLoader pbt = new ProgressBarDownLoader(connLength);//创建进度条
            new Thread(pbt).start();//开启线程,刷新进度条
            long startTime = System.currentTimeMillis();
            pbt.prevTime = System.currentTimeMillis();
            byte[] buffer = new byte[1204];
            long prevTime = System.currentTimeMillis();
            int bytePrev = 0;//前一次记录的文件大小
            int byteSum = 0;//总共读取的文件大小
            int byteRead;//每次读取的byte数
            while ((byteRead = inStream.read(buffer)) != -1) {
                byteSum += byteRead;

                //当前时间
                long currentTime = System.currentTimeMillis();
                int speed = 0;
                if (currentTime - prevTime > 0) {//避免两次读数太近,导致分母为0
                    speed = (int) ((byteSum - bytePrev) / (currentTime - prevTime));
                }
                if (MyPanel.limit > 0 && (connLength - byteSum) > MyPanel.limit) {//设置了限速
                    if (speed > MyPanel.limit) {
                        //计算出需要等待多久才能达到限速要求:
                        int sleepTime = (int) ((byteSum - bytePrev) / MyPanel.limit + prevTime - currentTime);
//                        System.out.println("sleep " + sleepTime + "s");
                        Thread.sleep(sleepTime);
                    }
                }
                fs.write(buffer, 0, byteRead);//读取
                pbt.updateProgress(byteRead);//写完一次,更新进度条
            }
            pbt.finish(System.currentTimeMillis() - startTime);//下载完毕
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值