`
天使的左手
  • 浏览: 55868 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

实现单线程的断点下载

阅读更多
/**
 * 实现单线程的断点下载
 */

public class HttpDownloadSingle implements Runnable
{
    // 响应状态码
    private String responseCode;
    // 响应头信息
    private Map<String, String> headers = new HashMap<String, String>();
    // 下载事件处理类
    private DownloadEvent event;
    // 下载文件的url
    private String url;
    // 文件保存在本地的路径
    private String outFilePath;
    // 缓冲区大小
    private int bufferSize;

    public HttpDownloadSingle(DownloadEvent event, String url, String outFilePath, int bufferSize)
    {
        this.event = event;
        this.url = url;
        this.outFilePath = outFilePath;
        this.bufferSize = bufferSize;
    }

    @Override
    public void run()
    {
        try
        {
            download();
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }

    public void download() throws IOException
    {
        // 下载的文件保存在本地的路径
        File outFile = new File(outFilePath);
        long finishedSize = 0;
        if (outFile.exists())
            // 如果文件已经存在,记录文件大小,即已下载大小
            finishedSize = outFile.length();

        URL download_url = new URL(url);
        Socket socket = new Socket();
        // 获取主机地址
        String host = download_url.getHost();
        // 获取端口 如果url地址没有指定端口 会返回-1 给定一个默认端口号80
        int port = download_url.getPort() == -1 ? 80 : download_url.getPort();
        // 资料路径
        String resourcePath = download_url.getPath();
        event.state("connecting " + host + ":" + port);
        // 设置read超时
        socket.setSoTimeout(5000);
        // 连接服务器 并且设置连接超时
        socket.connect(new InetSocketAddress(host, port), 3000);
        event.state("connect successfully");

        try
        {
            // 生成一个request消息,用于查看所下载文件大小和服务器是否支持断点下载
            generateHttpRequest(socket, host, resourcePath, finishedSize);

            InputStream socketIn = socket.getInputStream();
            // 解析响应头信息
            analyseResponseHeader(socketIn, event);

            // 获取要下载文件的大小
            long contentLength = getFileLength();

            // 如果已下载的大小大于等于总的文件大小 直接返回
            if (contentLength <= finishedSize)
                return;

            // 如果已下载大小不等于0 且状态码是200(表示服务器不支持断点下载
            // Accept-Ranges: bytes也可以用来判断服务器是否支持断点下载) 也直接返回
            if (finishedSize > 0 && "200".equals(responseCode))
                return;

            //206表示支持断点下载 其他状态码就抛出一个运行时异常
            if (responseCode.charAt(0) != '2')
                throw new RuntimeException("Unsupported response status code");

            byte[] buf = new byte[bufferSize];
            int n;
            FileOutputStream fos = new FileOutputStream(outFile, true);

            try
            {
                while (-1 != (n = socketIn.read(buf)))
                {
                    fos.write(buf, 0, n);
                    finishedSize += n;
                    //计算当前已下载百分比
                    event.percent(finishedSize * 100 / contentLength);
                }
            }
            finally
            {
                if (fos != null)
                {
                    try
                    {
                        fos.close();
                    }
                    catch (Exception e)
                    {
                    }
                }
            }
        }
        finally
        {
            socket.close();
        }
    }

    private String getHeader(String name)
    {
        return headers.get(name);
    }

    private int getIntHeader(String name)
    {
        return Integer.parseInt(getHeader(name).trim());
    }

    private long getFileLength()
    {
        long len = -1;
        try
        {
            len = getIntHeader("Content-Length");
            String[] parts = getHeader("Content-Range").split("/");
            if (parts.length > 1)
                len = Integer.parseInt(parts[1].trim());
            else
                len = -1;
        }
        catch (Exception e)
        {
        }
        return len;
    }

    private void analyseResponseHeader(InputStream socketIn, DownloadEvent event) throws IOException
    {
        String line = "";
        while (true)
        {
            int bt = socketIn.read();
            if (bt == '\r')
            {
                bt = socketIn.read();
                if (bt == '\n')
                {
                    //如果读到空行,就结束解析
                    if ("".equals(line))
                        break;
                    //解析状态行和头信息
                    addHeader2Map(line);
                    event.viewHeader(line);
                    line = "";
                }
            }
            else
                line += (char) bt;
        }
    }

    private void addHeader2Map(String line)
    {
        int index = line.indexOf(":");
        if (index > 0)
            headers.put(line.substring(0, index).trim(), line.substring(index + 1));
        else
            analyseResponseLine(line);
    }

    private void analyseResponseLine(String line)
    {
        String[] parts = line.split(" +");
        if (parts.length > 1)
            //获取状态码
            responseCode = parts[1].trim();
    }

    private void generateHttpRequest(Socket socket, String host, String path, long startPos) throws IOException
    {
        PrintWriter writer = new PrintWriter(socket.getOutputStream());
        writer.println("GET " + path + " HTTP/1.1");
        writer.println("Host: " + host);
        writer.println("User-Agent: java");
        if (startPos > 0)
            //如果已下载长度不为0 就通过Range头把已下载长度也一起发送给服务器
            writer.println("Range: bytes=" + startPos + "-");
        writer.println("Connection: close");
        writer.println();
        writer.flush();
    }

    public interface DownloadEvent
    {
        void state(String message);

        void viewHeader(String header);

        void percent(long newValue);
    }
}


class DownloadProgress implements DownloadEvent
{
    long oldValue = -1;

    @Override
    public void percent(long newValue)
    {
        if (newValue > oldValue)
        {
            System.out.print("[" + newValue + "%]");
            oldValue = newValue;
        }
    }

    @Override
    public void state(String message)
    {
        System.out.println(message);
    }

    @Override
    public void viewHeader(String header)
    {
        System.out.println(header);
    }
}

/*
 * 6688768 bytes 87494656 bytes 121552303 bytes
 */
public class Main
{
    public static void main(String[] args) throws Exception
    {

        if (args.length < 1)
        {
            System.out.println("Usage: java class DownloadFileName");
            return;
        }
        FileInputStream fis = new FileInputStream(args[0]);
        BufferedReader fileReader = new BufferedReader(new InputStreamReader(fis));
        String s = "";
        String[] ss;
        while ((s = fileReader.readLine()) != null)
        {
            ss = s.split("[ ]+");
            if (ss.length > 2)
            {
                System.out.println("---------------------------");
                System.out.println("downloading:" + ss[0]);
                System.out.println("output file path:" + ss[1]);
                System.out.println("buffer size:" + ss[2]);
                System.out.println("---------------------------");
                HttpDownloadSingle httpDownload = new HttpDownloadSingle(new DownloadProgress(), ss[0], ss[1], Integer
                        .parseInt(ss[2]));
                new Thread(httpDownload).start();
            }
        }
        fileReader.close();
    }
}


运行结果
---------------------------
downloading:http://localhost:8080/test_web/xx.exe
output file path:src/xx.exe
buffer size:4096
---------------------------
connecting localhost:8080
connect successfully
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"607195608-1397026780797"
Last-Modified: Wed, 09 Apr 2014 06:59:40 GMT
Content-Type: application/octet-stream
Content-Length: 607195608
Date: Wed, 16 Apr 2014 01:42:20 GMT
Connection: close
[0%][1%][2%][3%][4%][5%][6%][7%][8%][9%][10%][11%][12%][13%][14%][15%][16%][17%][18%]




分享到:
评论

相关推荐

    单线程断点续传

    在Android开发中,单线程断点续传是一项重要的技术,尤其在处理大文件下载时。这个技术的主要目的是提高用户体验,允许用户在不完整的下载过程中暂停、恢复下载,而不是重新开始整个过程。以下是对"单线程断点续传...

    android多线程断点下载

    本文将详细介绍如何在Android中实现多线程断点下载,以及如何封装一个易于使用的接口,使得即使是对Android不太熟悉的开发者也能快速上手。 首先,我们要理解什么是多线程下载。传统的单线程下载方式只有一个线程...

    Android多线程断点续传下载

    通过这个"MultiThreadDownload"项目,开发者可以学习到如何在Android中实现多线程断点续传下载,理解其背后的原理和技术细节,这对于提升应用的下载性能和用户体验非常有帮助。记得阅读源代码中的注释,它们会进一步...

    Android多线程断点下载(优化)

    在本文中,我们将深入探讨Android多线程断点下载的原理、实现方式以及优化策略。 首先,了解多线程的基本概念是必要的。在单线程下载中,数据流通常是顺序的,如果网络状况不佳,整个下载过程将受到影响。而多线程...

    多线程断点下载

    通过以上讲解,我们可以看到多线程断点下载技术在Java和Android平台上的实现涉及了网络编程、多线程同步、文件操作等多个方面的知识,它在提升用户体验的同时,也对开发者的技术要求较高。在实际应用中,开发者需要...

    c/c++线程断点续传实现

    在C/C++编程中,实现线程断点续传是一项技术挑战,特别是在处理大文件下载时,这种功能显得尤为重要。断点续传允许程序在中断后从上次停止的地方继续下载,提高了用户体验并节省了网络资源。Cocos2dx是一个基于C++的...

    Java多线程断点下载Sample

    在这个“Java多线程断点下载Sample”示例中,我们将深入探讨如何利用Java的多线程特性来实现文件的断点续传下载,并结合进度条展示下载进度。 首先,我们需要理解什么是多线程。在单线程环境中,程序的执行顺序是...

    Android单线程断点续传实例

    下面我们将详细探讨Android单线程断点续传的实现原理及步骤。 首先,我们要理解断点续传的基本概念。在下载过程中,服务器会提供文件的总大小,客户端(即Android设备)保存当前已下载的数据量,当再次启动下载时,...

    详解Android中的多线程断点下载

    多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序“拼接”起来就构 成了完整的文件了。这样就大大提高了文件的下载...

    htp多线程断点下载文件

    了解并实现多线程断点下载文件的原理和技术,不仅可以提升开发者的技能,也能帮助用户更高效地管理和下载大型文件,从而提高工作效率。在实际开发中,还需要考虑如何优化资源分配,防止过多线程对系统造成过大的压力...

    OKHttp多线程断点下载

    【OKHttp多线程断点下载】是一种在Android或Java应用中实现高效文件下载的方法,它结合了OKHttp网络库的优秀性能与多线程技术,以提高下载速度,并允许在下载过程中中断和恢复,避免因网络问题或其他因素导致的下载...

    多线程断点下载文件

    总之, MulThreadDownloader 多线程断点下载数据技术是Android平台提高文件下载效率和用户体验的有效手段,通过合理地利用多线程和断点续传技术,克服了单一下载方式的局限性,实现了高效、可靠的文件下载。

    Android中实现多线程断点下载

    实现多线程断点下载涉及的技术点广泛,包括多线程编程、网络请求、文件I/O、数据持久化以及UI交互。开发者需要对Android系统有深入的理解,同时熟悉相关库的使用。实践中,可以借助第三方库如Volley、AsyncTask、...

    android 多线程断点下载 (ListView 模式)

    本教程主要聚焦于如何实现一个具有开始、暂停功能,并集成到ListView中的多线程断点下载系统。 1. **多线程下载原理** 多线程下载是指将一个大文件分成多个小部分,每个部分由一个单独的线程负责下载。这样可以...

    android 多线程断点下载及进度实时更新demo

    通过阅读和理解这个代码,开发者可以学习如何在Android中实现多线程断点下载和进度更新。 总之,Android的多线程断点下载与进度实时更新是提升用户体验和下载效率的重要手段。开发者需要掌握网络编程、多线程、文件...

    安卓文件下载上传解压相关-android下载框架支持单线程和多线程断点下载.rar

    本压缩包中的资源涉及到了一个Android文件下载框架,它支持单线程和多线程的断点续传功能。理解这些概念和技术对于Android开发者来说至关重要。 1. **文件下载** 文件下载通常涉及到网络请求和数据流的处理。在...

    java多线程断点下载

    实现Java多线程断点下载的步骤如下: 1. **检查已下载部分**:首先,我们需要读取本地已有的文件,获取其当前大小,这将作为断点续传的起点。 2. **创建线程池**:使用`ExecutorService`创建一个线程池,线程数量...

Global site tag (gtag.js) - Google Analytics