安卓 多线程下载文件

Posted zhen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓 多线程下载文件相关的知识,希望对你有一定的参考价值。

HTTP文件多线程下载

测试代码

    String downloadUrl = "http://192.168.31.162/FileServer/SoftApk/UC-11.5.5.943.apk";
    String filepath = PathUtils.getCachePath() + "UC-11.5.5.943.apk";
    /**
     * 多线程下载
     *  基于:http://blog.csdn.net/mad1989/article/details/38421465
     *
     */
    private void startMutitaskDownload(){
        DownloadTask dlTask = new DownloadTask(downloadUrl, filepath, 5, new DownloadTask.DLListener() {
            @Override
            public void onDLStart() {

            }

            @Override
            public void onDLFinished(final long costTime, final long dlSize) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        DecimalFormat dFormat   =   new   DecimalFormat("##0.00");
                        Double res = dlSize * 1.0 / 1024 / costTime;
                        String speed = dFormat.format(res);
                        tvDlAverage.setText("平均下载速度:" + speed + "Mb/s ");
                    }
                });
            }

            @Override
            public void onProgressChange(final long fileSize, final long dlSize) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvDlInfo.setText("下载速度:" + dlSize / 1024 / 1024 + "Mb/s " +
                                "下载进度:" + dlSize / (fileSize / 100) + "%");
                    }
                });
            }
        });
        dlTask.start();
    }

 

DownloadTask.java
技术分享
import java.io.File;
import java.net.URL;
import java.net.URLConnection;
import java.util.Timer;
import java.util.TimerTask;

import utils.MLog;

/**
 * Created by e2670 on 2017/9/10.
 * FtpDownloadTask 多线程下载任务分配
 */

public class DownloadTask extends Thread{
    private String downloadUrl;
    private String filePath;
    private int threadNum;
    private long fileSize;
    private int downloadedAllSize = 0;  // 当前所有线程下载总量

    boolean isFinished = false;

    public DownloadTask(String downloadUrl, String filePath, int threadNum, DLListener dlListener) {
        this.downloadUrl = downloadUrl;
        this.threadNum = threadNum;
        this.filePath = filePath;
        this.dlListener = dlListener;
    }

    @Override
    public void run() {
        final DLThread[] threads = new DLThread[threadNum];
        try {
            URL url = new URL(downloadUrl);
            MLog.d(downloadUrl);
            URLConnection conn = url.openConnection();
            // 读取下载文件总大小
            fileSize = conn.getContentLength();
            if (fileSize == -1) { // nginx文件服务器
                fileSize = Long.valueOf(conn.getHeaderField("Content-Length"));
            }
            if (fileSize <= 0) {
                MLog.e("读取文件失败:文件大小" + fileSize);
                return;
            }
            // 计算每条线程下载的数据长度
            long blockSize = (fileSize % threadNum) == 0 ? fileSize / threadNum
                    : fileSize / threadNum + 1;
            MLog.d("threadNum:" + threadNum + "  fileSize:" + fileSize + "  blockSize:" + blockSize);

            File file = new File(filePath);
            for (int i = 0; i < threads.length; i++) {
                // 启动线程,分别下载每个线程需要下载的部分
                threads[i] = new DLThread(url, file, blockSize, (i + 1));
                threads[i].setName("FtpDLThread:" + i);
                threads[i].start();
            }
            final long startTime = System.currentTimeMillis();
            dlListener.onDLStart();
            // 每秒统计一次已下载文件大小
            final Timer timer = new Timer();
            TimerTask timerTask = new TimerTask() {
                @Override
                public void run() {
                    isFinished = true;
                    downloadedAllSize = 0;
                    for (int i = 0; i < threads.length; i++) {
                        downloadedAllSize += threads[i].getDownloadLength();
                        if (!threads[i].isCompleted()) {
                            isFinished = false;
                        }
                    }
                    dlListener.onProgressChange(fileSize, downloadedAllSize);
                    if(isFinished){
                        timer.cancel();
                        dlListener.onDLFinished(System.currentTimeMillis() - startTime,downloadedAllSize);
                    }
                }
            };
            timer.schedule(timerTask,1000,1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private DLListener dlListener;

    public interface DLListener {
        void onDLStart();

        void onDLFinished(long costTime,long dlSize);

        void onProgressChange(long fileSize, long dlSize);
    }
}
View Code
DLThread.java
技术分享
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;

import utils.MLog;

/**
 * Created by e2670 on 2017/9/10.
 * FtpDLThread 完成每个独立线程的下载任务
 */
public class DLThread extends Thread {

    private static final String TAG = DLThread.class.getSimpleName();

    /**
     * 当前下载是否完成
     */
    private boolean isCompleted = false;
    /**
     * 当前下载文件长度
     */
    private long downloadLength = 0;
    /**
     * 文件保存路径
     */
    private File file;
    /**
     * 文件下载路径
     */
    private URL downloadUrl;
    /**
     * 当前下载线程ID
     */
    private int threadId;
    /**
     * 线程下载数据长度
     */
    private long blockSize;

    private int cacheSize = 100 * 1024;

    /**
     * @param downloadUrl:文件下载地址
     * @param file:文件保存路径
     * @param blockSize:下载数据长度
     * @param threadId:线程ID
     */
    public DLThread(URL downloadUrl, File file, long blockSize,
                    int threadId) {
        this.downloadUrl = downloadUrl;
        this.file = file;
        this.threadId = threadId;
        this.blockSize = blockSize;
    }

    @Override
    public void run() {
        BufferedInputStream bis = null;
        RandomAccessFile raf = null;

        try {
            URLConnection conn = downloadUrl.openConnection();
            conn.setAllowUserInteraction(true);

            long startPos = blockSize * (threadId - 1); //开始位置
            long endPos = blockSize * threadId - 1; //结束位置
            //设置当前线程下载的起点、终点
            conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
            MLog.d(Thread.currentThread().getName() + "  bytes=" + startPos + "-" + endPos);

            byte[] buffer = new byte[cacheSize];
            bis = new BufferedInputStream(conn.getInputStream());

            raf = new RandomAccessFile(file, "rwd");
            raf.seek(startPos);
            int len;
            while ((len = bis.read(buffer, 0, cacheSize)) != -1) {
                raf.write(buffer, 0, len);
                downloadLength += len;
            }
            isCompleted = true;
            MLog.d(TAG, "current thread task has finished,all size:" + downloadLength);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 线程文件是否下载完毕
     */
    public boolean isCompleted() {
        return isCompleted;
    }

    /**
     * 线程下载文件长度
     */
    public long getDownloadLength() {
        return downloadLength;
    }

}
View Code

 

FTP文件多线程下载

测试代码

Apache Commons Net 3.6
    String downloadUrl = "ftp://[email protected]/SoftApk/UC-11.5.5.943.apk";
    String filepath = PathUtils.getCachePath() + "UC-11.5.5.943.apk";
    /**
     * 多线程下载
     *  基于:http://blog.csdn.net/qwe511455842/article/details/76603675
     *
     */
    private void startMutitaskDownload(){
        FtpDownloadTask dlTask = new FtpDownloadTask(downloadUrl, filepath, 5, new FtpDownloadTask.DLListener() {
            @Override
            public void onDLStart() {

            }

            @Override
            public void onDLFinished(final long costTime, final long dlSize) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        DecimalFormat dFormat   =   new   DecimalFormat("##0.00");
                        Double res = dlSize * 1.0 / 1024 / costTime;
                        String speed = dFormat.format(res);
                        tvDlAverage.setText("平均下载速度:" + speed + "Mb/s ");
                    }
                });
            }

            @Override
            public void onProgressChange(final long fileSize, final long dlSize) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tvDlInfo.setText("下载速度:" + dlSize / 1024 / 1024 + "Mb/s " +
                                "下载进度:" + dlSize / (fileSize / 100) + "%");
                    }
                });
            }
        });
        dlTask.start();
    }
FtpDownloadTask.java
技术分享
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

import utils.MLog;

/**
 * Created by e2670 on 2017/9/10.
 * FtpDownloadTask 多线程下载任务分配
 */

public class FtpDownloadTask extends Thread{
    private String downloadUrl;
    private String filePath;
    private int threadNum;
    private long fileSize;
    private int downloadedAllSize = 0;  // 当前所有线程下载总量

    boolean isFinished = false;

    public FtpDownloadTask(String downloadUrl, String filePath, int threadNum, DLListener dlListener) {
        this.downloadUrl = downloadUrl;
        this.threadNum = threadNum;
        this.filePath = filePath;
        this.dlListener = dlListener;
    }

    @Override
    public void run() {
        final FtpDLThread[] threads = new FtpDLThread[threadNum];
        try {
            MLog.d(downloadUrl);
            FTPClient client = new FTPClient();
            FtpLogin ftpLogin;
            try {
                ftpLogin = parseFtpUrl(downloadUrl);
            }catch (Exception e){
                e.printStackTrace();
                MLog.e("ftp url解析错误");
                return;
            }
            client.connect(ftpLogin.ipAddr,ftpLogin.port);
            if(ftpLogin.userName != null){
                client.login(ftpLogin.userName, ftpLogin.password);
            }

            int reply = client.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                client.disconnect();
                MLog.e("无法连接到ftp服务器,错误码为:" + reply);
                return;
            }

            String charSet = "UTF-8";
            if (!FTPReply.isPositiveCompletion(client.sendCommand("OPTS UTF8", "ON"))) {
                charSet = "GBK";
            }
            FTPFile[] files = client.listFiles(new String(ftpLogin.path.getBytes(charSet), "ISO-8859-1"));
            if(files.length > 0){
                fileSize = files[0].getSize();
            }

            if (fileSize <= 0) {
                MLog.e("读取文件失败:文件大小" + fileSize);
                return;
            }
            // 计算每条线程下载的数据长度
            long blockSize = (fileSize % threadNum) == 0 ? fileSize / threadNum
                    : fileSize / threadNum + 1;
            MLog.d("threadNum:" + threadNum + "  fileSize:" + fileSize + "  blockSize:" + blockSize);

            File file = new File(filePath);
            for (int i = 0; i < threads.length; i++) {
                // 启动线程,分别下载每个线程需要下载的部分
                threads[i] = new FtpDLThread(ftpLogin, file, blockSize, (i + 1));
                threads[i].setName("FtpDLThread:" + i);
                threads[i].start();
            }
            final long startTime = System.currentTimeMillis();
            dlListener.onDLStart();
            // 每秒统计一次已下载文件大小
            final Timer timer = new Timer();
            TimerTask timerTask = new TimerTask() {
                @Override
                public void run() {
                    isFinished = true;
                    downloadedAllSize = 0;
                    for (int i = 0; i < threads.length; i++) {
                        downloadedAllSize += threads[i].getDownloadLength();
                        if (!threads[i].isCompleted()) {
                            isFinished = false;
                        }
                    }
                    dlListener.onProgressChange(fileSize, downloadedAllSize);
                    if(isFinished){
                        timer.cancel();
                        dlListener.onDLFinished(System.currentTimeMillis() - startTime,downloadedAllSize);
                    }
                }
            };
            timer.schedule(timerTask,1000,1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * FTP url解析
     * @param fullUrl
     * @return
     */
    private FtpLogin parseFtpUrl(String fullUrl) throws Exception{
        FtpLogin ftpLogin = new FtpLogin();
        fullUrl = fullUrl.trim();
        String res;
        int beginIndex = "ftp://".length();
        int endIndex = fullUrl.indexOf(‘/‘, beginIndex);
        if(endIndex != -1){
            res =  fullUrl.substring(beginIndex, endIndex);
            String[] splitRes = res.split("@");
            if(splitRes.length > 1){    // IP+Login
                String[] splitIp = splitRes[1].split(":");
                ftpLogin.ipAddr = splitIp[0];
                if (splitIp.length > 1){
                    ftpLogin.port = Integer.parseInt(splitIp[1]);
                }else {
                    ftpLogin.port = 21;
                }
                String[] splitUser = splitRes[0].split(":");
                ftpLogin.userName = splitUser[0];
                if (splitIp.length > 1){
                    ftpLogin.password = splitUser[1];
                }else {
                    ftpLogin.password = "";
                }
            }else { // IP
                String[] splitIp = splitRes[0].split(":");
                ftpLogin.ipAddr = splitIp[0];
                if (splitIp.length > 1){
                    ftpLogin.port = Integer.parseInt(splitIp[1]);
                }else {
                    ftpLogin.port = 21;
                }
            }
            ftpLogin.path = fullUrl.substring(endIndex);
        }else {
            throw new Exception("URL解析错误");
        }
        return ftpLogin;
    }

    private DLListener dlListener;

    public interface DLListener {
        void onDLStart();

        void onDLFinished(long costTime, long dlSize);

        void onProgressChange(long fileSize, long dlSize);
    }
}
View Code
FtpDLThread.java
技术分享
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import utils.MLog;

/**
 * Created by e2670 on 2017/9/10.
 * FtpDLThread 完成每个独立线程的下载任务
 */
public class FtpDLThread extends Thread {

    private static final String TAG = FtpDLThread.class.getSimpleName();

    /**
     * 当前下载是否完成
     */
    private boolean isCompleted = false;
    /**
     * 当前下载文件长度
     */
    private long downloadLength = 0;
    /**
     * 文件保存路径
     */
    private File file;
    /**
     * 文件下载路径
     */
    private FtpLogin ftpLogin;
    /**
     * 当前下载线程ID
     */
    private int threadId;
    /**
     * 线程下载数据长度
     */
    private long blockSize;

    private int cacheSize = 100 * 1024;

    /**
     * @param ftpLogin:文件下载地址
     * @param file:文件保存路径
     * @param blockSize:下载数据长度
     * @param threadId:线程ID
     */
    public FtpDLThread(FtpLogin ftpLogin, File file, long blockSize,
                       int threadId) {
        this.ftpLogin = ftpLogin;
        this.file = file;
        this.threadId = threadId;
        this.blockSize = blockSize;
    }

    @Override
    public void run() {
        BufferedInputStream bis = null;
        RandomAccessFile raf = null;

        try {
            FTPClient client = new FTPClient();
            client.connect(ftpLogin.ipAddr, ftpLogin.port);
            if (ftpLogin.userName != null) {
                client.login(ftpLogin.userName, ftpLogin.password);
            }

            client.enterLocalPassiveMode();     //设置被动模式
            client.setFileType(FTP.BINARY_FILE_TYPE);  //设置文件传输模式
            long startPos = blockSize * (threadId - 1); //开始位置
            long endPos = blockSize * threadId - 1;     //结束位置
            client.setRestartOffset(startPos);   //设置恢复下载的位置
            MLog.d(Thread.currentThread().getName() + "  bytes=" + startPos + "-" + endPos);

            String charSet = "UTF-8";
            if (!FTPReply.isPositiveCompletion(client.sendCommand("OPTS UTF8", "ON"))) {
                charSet = "GBK";
            }
            client.allocate(cacheSize);
            InputStream is = client.retrieveFileStream(new String(ftpLogin.path.getBytes(charSet), "ISO-8859-1"));

            int reply = client.getReplyCode();
            if (!FTPReply.isPositivePreliminary(reply)) {
                client.disconnect();
                MLog.e("获取文件信息错误,错误码为:" + reply, null);
                return;
            }

            byte[] buffer = new byte[cacheSize];
            bis = new BufferedInputStream(is);

            raf = new RandomAccessFile(file, "rwd");
            raf.seek(startPos);
            int len;
            while ((len = bis.read(buffer, 0, cacheSize)) != -1) {
                raf.write(buffer, 0, len);
                downloadLength += len;
                if (downloadLength >= endPos - startPos){
                    break;
                }
            }
            isCompleted = true;
            MLog.d(TAG, "current thread task has finished,all size:" + downloadLength);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 线程文件是否下载完毕
     */
    public boolean isCompleted() {
        return isCompleted;
    }

    /**
     * 线程下载文件长度
     */
    public long getDownloadLength() {
        return downloadLength;
    }

}
View Code
FtpLogin.java
技术分享
public class FtpLogin {
    public String userName;
    public String password;
    public String ipAddr;
    public int port;
    public String path;
}
View Code

 

以上是关于安卓 多线程下载文件的主要内容,如果未能解决你的问题,请参考以下文章

Android 多线程下载,断点续传,线程池

高分求解,多线程下载工具问题!

C# Winform 多线程 SOCKET 文件上传,下载

安卓实训第七天---多线程下载实现(进度条)

08_多线程下载原理

C#实现的HttpListener服务器怎么支持多线程下载