多线程下载

简介: 楼主三年磨剑(当然不是磨着一把剑),倾血奉献Android多线程下载Demo。有的人就问了“怎么写来写去还是Demo?”,因为老哥我实在太忙了, 每天写一点,写到现在也才写了个下载器,也就差下载管理类就是个完整的模块了。对于新手学习这已经足够了,不对,是完全足够了。 这不仅仅只是一个简单的Demo,这绝对是你前所未见的商业级别的范例,集支持多线程下载,断点续传,只使用wifi网

楼主三年磨剑(当然不是磨着一把剑),倾血奉献Android多线程下载Demo。有的人就问了“怎么写来写去还是Demo?”,因为老哥我实在太忙了,
每天写一点,写到现在也才写了个下载器,也就差下载管理类就是个完整的模块了。对于新手学习这已经足够了,不对,是完全足够了。

这不仅仅只是一个简单的Demo,这绝对是你前所未见的商业级别的范例,集支持多线程下载,断点续传,只使用wifi网络下载,显示下载速度,人性化提示
及超强的容错机制多功能于一体,绝对的实用,绝对的专业。

当然我写这个是为了下载apk的,大家稍微改一改就可以写成更通用的下载器。唯一有点不足的地方就是在Android上使用RandomAccessFile在创建大文件的时候
速度有些慢,导致前几秒的进度都为0。不知道有没有人可以帮我解决这个问题。

下面给出关键代码。

[java]  view plain copy
  1. package com.h3c.DownloadEngine;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.RandomAccessFile;  
  7. import java.net.HttpURLConnection;  
  8. import java.net.URL;  
  9. import java.util.ArrayList;  
  10.   
  11. import android.content.Context;  
  12. import android.os.Environment;  
  13. import android.util.Log;  
  14.   
  15. import com.h3c.DownloadEngine.common.DownloaderErrorException;  
  16. import com.h3c.DownloadEngine.common.EngineConstants;  
  17. import com.h3c.DownloadEngine.common.EngineUtil;  
  18. import com.h3c.DownloadEngine.common.EngineVariable;  
  19. import com.h3c.DownloadEngine.db.EngineDBOperator;  
  20. import com.h3c.DownloadEngine.entity.DownloadBean;  
  21.   
  22. public class Downloader {  
  23.     private final static String TAG = "Downloader";  
  24.     private final static byte[] lock_getFileSize = new byte[1];  
  25.     private final static byte[] lock_refresh_progress = new byte[1];  
  26.   
  27.     private int mThreadCount = 4;// 默认子线程数为4个  
  28.     private int bufferSize = 1024 * 16// 16K 一个块  
  29.   
  30.     private DownloadBean mBean;// 注意,这里是dwonloader的bean不是其subBean  
  31.     private Context mContext;  
  32.     private DownloadEngineCallback mCallback;  
  33.     private EngineDBOperator mDBOper;  
  34.     private int mDoneThreadCount = 0;// 完成的线程数  
  35.     private int mState = EngineConstants.DOWNLOAD_STATE_INIT;// 下载器状态  
  36.     private ArrayList<DownloadBean> mBeans = new ArrayList<DownloadBean>(  
  37.             mThreadCount);  
  38.   
  39.     public Downloader(DownloadBean bean, Context context,  
  40.             DownloadEngineCallback callback) throws DownloaderErrorException {  
  41.         this.mBean = bean;  
  42.         this.mContext = context;  
  43.         this.mCallback = callback;  
  44.         this.mDBOper = EngineDBOperator.getInstance(context);  
  45.   
  46.         if (this.mDBOper != null) {  
  47.             if (this.mDBOper.isHasDownloadTaskByUrl(bean.url)) {// 如果此任务已经存放进数据库  
  48.                 getDownloaderInfoFromDB(bean);  
  49.             } else {// 插入信息至数据库  
  50.                 addDownloaderInfoToDB(bean);  
  51.             }  
  52.         } else {  
  53.             callBackError("Downloader错误,可能是EngineDBOperator为Null.");  
  54.             throw new DownloaderErrorException(  
  55.                     "Downloader错误,可能是EngineDBOperator为Null.");  
  56.         }  
  57.     }  
  58.   
  59.     public DownloadBean getDownloaderInfo() {  
  60.         return mBean;  
  61.     }  
  62.   
  63.     public int getDownloaderState() {  
  64.         return mState;  
  65.     }  
  66.   
  67.     /** 
  68.      * 请求初始化 
  69.      *  
  70.      * @param state 
  71.      */  
  72.     protected void setDownloaderState(int state) {  
  73.         mState = state;  
  74.         if (state == EngineConstants.DOWNLOAD_STATE_INIT) {  
  75.             mBean.currentPosition = 0;  
  76.         }  
  77.     }  
  78.   
  79.     /** 
  80.      * 加入下载信息进入数据库,此方法用于刚刚初始化Downloader,且数据库中没有该任务的时候 
  81.      *  
  82.      * @param bean 
  83.      * @throws DownloaderErrorException 
  84.      */  
  85.     private void addDownloaderInfoToDB(DownloadBean bean)  
  86.             throws DownloaderErrorException {  
  87.         if (mState != EngineConstants.DOWNLOAD_STATE_INIT  
  88.                 && mState != EngineConstants.DOWNLOAD_STATE_STOP  
  89.                 && mState != EngineConstants.DOWNLOAD_STATE_ERROR) {  
  90.             callBackError("这个任务已经加入到数据库中了");  
  91.             throw new DownloaderErrorException("这个任务已经加入到数据库中了");  
  92.         }  
  93.   
  94.         if (mDBOper != null) {  
  95.             long fileSize = bean.fileSize;  
  96.             if (mBeans.size() > 0) {  
  97.                 mBeans.clear();  
  98.             }  
  99.   
  100.             try {  
  101.                 if (fileSize > 0) {// 判断传入的fileSize大小,如果大于0,就不用从网络中获取,直接初始化N个子下载器  
  102.                     if (!hasSpaceInSDCard()) {  
  103.                         return;  
  104.                     }  
  105.                     long range = fileSize / mThreadCount;// 文件分段值  
  106.                     for (int i = 0; i < mThreadCount - 1; i++) {  
  107.                         DownloadBean subBean = (DownloadBean) bean.clone();  
  108.                         subBean.threadId = i;  
  109.                         subBean.startPosition = i * range;  
  110.                         subBean.endPosition = (i + 1) * range - 1;  
  111.                         mBeans.add(subBean);  
  112.                     }  
  113.   
  114.                     DownloadBean subBean = (DownloadBean) bean.clone();  
  115.                     subBean.threadId = mThreadCount - 1;  
  116.                     subBean.startPosition = (mThreadCount - 1) * range;  
  117.                     subBean.endPosition = fileSize - 1;  
  118.                     mBeans.add(subBean);  
  119.                 } else {// 如果等于0,就直接初始化N个0大小的子下载器  
  120.                     for (int n = 0; n < mThreadCount - 1; n++) {  
  121.                         DownloadBean subBean = (DownloadBean) bean.clone();  
  122.                         subBean.threadId = n;  
  123.                         mBeans.add(subBean);  
  124.                     }  
  125.   
  126.                     DownloadBean subBean = (DownloadBean) bean.clone();  
  127.                     subBean.threadId = mThreadCount - 1;  
  128.                     mBeans.add(subBean);  
  129.                 }  
  130.   
  131.                 mDBOper.addDownloadTask(mBeans);  
  132.                 if (bean.fileSize > 0) {// 如果文件大小已经获取就进入等待状态  
  133.                     mState = EngineConstants.DOWNLOAD_STATE_WAITTING;// 下载器进入等待状态  
  134.                 } else {// 文件大小未获取就开启线程去获取文件大小并更新子下载器中的内容  
  135.                     new Thread(new Runnable() {  
  136.                         @Override  
  137.                         public void run() {  
  138.                             boolean flag = false;  
  139.                             synchronized (lock_getFileSize) {  
  140.                                 flag = getFileSizeByNetwork(mBean);  
  141.                             }  
  142.                             if (flag) {  
  143.                                 mState = EngineConstants.DOWNLOAD_STATE_WAITTING;// 下载器进入等待状态  
  144.                             } else {  
  145.                                 Log.e(TAG, "从网络中获取文件大小失败 1");  
  146.                             }  
  147.                         }  
  148.                     }).start();  
  149.   
  150.                 }  
  151.             } catch (CloneNotSupportedException e) {  
  152.                 e.printStackTrace();  
  153.             }  
  154.         } else {  
  155.             callBackError("addDownloaderInfoToDB错误,可能是EngineDBOperator为Null.");  
  156.             throw new DownloaderErrorException(  
  157.                     "addDownloaderInfoToDB错误,可能是EngineDBOperator为Null.");  
  158.         }  
  159.     }  
  160.   
  161.     /** 
  162.      * 从数据库中读取下载器信息 
  163.      *  
  164.      * @param bean 
  165.      * @throws DownloaderErrorException 
  166.      */  
  167.     private void getDownloaderInfoFromDB(DownloadBean bean)  
  168.             throws DownloaderErrorException {  
  169.         if (mDBOper != null) {  
  170.             mBeans.clear();  
  171.             mBeans = mDBOper.getDownloadTaskByUrl(bean.url);  
  172.   
  173.             mBean.currentPosition = 0;  
  174.             mBean.fileSize = 0;  
  175.             mThreadCount = mBeans.size();  
  176.             for (DownloadBean subBean : mBeans) {  
  177.                 mBean.currentPosition += subBean.currentPosition;  
  178.                 if (subBean.fileSize > mBean.fileSize) {  
  179.                     mBean.fileSize = subBean.fileSize;  
  180.                 }  
  181.             }  
  182.   
  183.             if (mBean.fileSize < 1) {  
  184.                 new Thread(new Runnable() {  
  185.                     @Override  
  186.                     public void run() {  
  187.                         boolean flag = false;  
  188.                         synchronized (lock_getFileSize) {  
  189.                             flag = getFileSizeByNetwork(mBean);  
  190.                         }  
  191.                         if (flag) {  
  192.                             mState = EngineConstants.DOWNLOAD_STATE_WAITTING;// 下载器进入等待状态  
  193.                         } else {  
  194.                             Log.e(TAG, "从网络中获取文件大小失败 2");  
  195.                         }  
  196.                     }  
  197.                 }).start();  
  198.             } else {  
  199.                 mState = EngineConstants.DOWNLOAD_STATE_WAITTING;// 下载器进入等待状态  
  200.             }  
  201.         } else {  
  202.             callBackError("getDownloaderInfoFromDB Error,May be EngineDBOperator is Null.");  
  203.             throw new DownloaderErrorException(  
  204.                     "getDownloaderInfoFromDB Error,May be EngineDBOperator is Null.");  
  205.         }  
  206.     }  
  207.   
  208.     /** 
  209.      * 从网络中获取文件大小,并更新listBeans 
  210.      */  
  211.     private boolean getFileSizeByNetwork(DownloadBean bean) {  
  212.         HttpURLConnection connection = null;  
  213.         long fileSize = bean.fileSize;  
  214.         try {  
  215.             if (fileSize <= 0) {// 如果没有传入文件大小就从网络中获取  
  216.                 URL url = new URL(bean.url);  
  217.                 connection = (HttpURLConnection) url.openConnection();  
  218.                 connection.setConnectTimeout(5000);  
  219.                 connection.setReadTimeout(8000);  
  220.   
  221.                 if (android.os.Build.VERSION.SDK_INT > 10) {// 规避2.x上因为加入setRM导致连接超时的bug  
  222.                     connection.setRequestMethod("HEAD");// head  
  223.                 }  
  224.   
  225.                 int resopnseCode = connection.getResponseCode();  
  226.                 if (resopnseCode != 200 && resopnseCode != 206) {  
  227.                     callBackError("http返回码不正确:" + resopnseCode);  
  228.                     return false;  
  229.                 }  
  230.                 // 获得文件大小  
  231.                 fileSize = connection.getContentLength();  
  232.                 mBean.fileSize = fileSize;  
  233.   
  234.                 if (fileSize <= 0) {  
  235.                     callBackError("无法从服务器上获得文件大小" + fileSize);  
  236.                     return false;  
  237.                 }  
  238.   
  239.                 // if (connection.getHeaderField("Content-Range") == null) {  
  240.                 // Log.e(TAG, "服务器不支持断点续传");  
  241.                 // mThreadCount = 1;  
  242.                 // }  
  243.   
  244.                 // 如果没有存储空间了  
  245.                 if (!hasSpaceInSDCard()) {  
  246.                     return false;  
  247.                 }  
  248.   
  249.                 long range = fileSize / mThreadCount;// 文件分段值  
  250.                 // 更新listBean  
  251.                 for (int i = 0; i < mThreadCount - 1; i++) {  
  252.                     DownloadBean subBean = mBeans.get(i);  
  253.                     subBean.fileSize = fileSize;  
  254.                     subBean.startPosition = i * range;  
  255.                     subBean.endPosition = (i + 1) * range - 1;  
  256.                 }  
  257.   
  258.                 DownloadBean subBean = mBeans.get(mThreadCount - 1);  
  259.                 subBean.fileSize = fileSize;  
  260.                 subBean.startPosition = (mThreadCount - 1) * range;  
  261.                 subBean.endPosition = fileSize - 1;  
  262.   
  263.                 // 更新数据库  
  264.                 if (mDBOper != null) {  
  265.                     mDBOper.updateTaskCompleteSize(mBeans, mBean.url);  
  266.                 } else {  
  267.                     callBackError("getFileSizeByNetwork错误,可能是EngineDBOperator is Null.");  
  268.                     throw new DownloaderErrorException(  
  269.                             "getFileSizeByNetwork错误,可能是EngineDBOperator is Null.");  
  270.                 }  
  271.                 return true;  
  272.             } else {// 文件有大小就直接退出  
  273.                 return true;  
  274.             }  
  275.         } catch (Exception e) {  
  276.             callBackError("从服务器获取文件大小超时");  
  277.             e.printStackTrace();  
  278.         } finally {  
  279.             if (connection != null) {  
  280.                 connection.disconnect();  
  281.             }  
  282.         }  
  283.         return false;  
  284.     }  
  285.   
  286.     /** 
  287.      * 开始下载,可能多次调用 
  288.      */  
  289.     public void startDownloader() {  
  290.         if (mState == EngineConstants.DOWNLOAD_STATE_DOWNLOADING) {// 如果正在下载就return  
  291.             return;  
  292.         }  
  293.   
  294.         if (mBean == null) {  
  295.             callBackError("下载器没有初始化");  
  296.             return;  
  297.         }  
  298.   
  299.         File file = new File(mBean.savePath);  
  300.         File parentDirectory = file.getParentFile();  
  301.         if (!parentDirectory.exists()) {  
  302.             parentDirectory.mkdirs();  
  303.         }  
  304.   
  305.         if (!file.exists()) {  
  306.             try {  
  307.                 file.createNewFile();  
  308.             } catch (IOException e) {  
  309.                 e.printStackTrace();  
  310.             }  
  311.         }  
  312.           
  313.         if (mBeans.size() < 1) {// 防止由于发生错误导致清空了mBeans列表,但是又重新开始了任务,所有要再次初始化mBeans  
  314.             try {  
  315.                 addDownloaderInfoToDB(mBean);  
  316.             } catch (DownloaderErrorException e) {  
  317.                 e.printStackTrace();  
  318.                 return;  
  319.             }  
  320.         }  
  321.   
  322.         /** 
  323.          * 只有获得文件大小后才会开始下载 
  324.          */  
  325.         synchronized (lock_getFileSize) {  
  326.             if (mState == EngineConstants.DOWNLOAD_STATE_INIT) {// 获取文件大小失败,重新获取  
  327.                 boolean flag = getFileSizeByNetwork(mBean);  
  328.                 if (!flag) {  
  329.                     callBackError("获取文件大小失败");  
  330.                     return;  
  331.                 }  
  332.             }  
  333.         }  
  334.   
  335.         mState = EngineConstants.DOWNLOAD_STATE_DOWNLOADING;  
  336.         mDBOper.removePauseFileByUrl(mBean.url);// 从暂停列表中移除  
  337.         mDoneThreadCount = 0;// 初始化完成线程数  
  338.   
  339.         for (DownloadBean bean : mBeans) {  
  340.             if (bean.currentPosition < (bean.endPosition - bean.startPosition)) {// 如果该线程属于没有下载完成的  
  341.                 HamalThread hamalThread = new HamalThread(bean);  
  342.                 hamalThread.start();  
  343.             } else {// 已经完成的线程不需要重新创建  
  344.                 mDoneThreadCount++;  
  345.             }  
  346.         }  
  347.           
  348.         if (mDoneThreadCount == mThreadCount) {// 下载完成  
  349.             downloaderDone();  
  350.         }  
  351.     }  
  352.   
  353.     private class HamalThread extends Thread {  
  354.         private int threadId;  
  355.         private long startPos;  
  356.         private long endPos;  
  357.         private long compeleteSize;  
  358.         private String urlstr;  
  359.   
  360.         public HamalThread(DownloadBean bean) {  
  361.             this.threadId = bean.threadId;  
  362.             this.startPos = bean.startPosition;  
  363.             this.endPos = bean.endPosition;  
  364.             this.compeleteSize = bean.currentPosition;  
  365.             this.urlstr = bean.url;  
  366.         }  
  367.   
  368.         @Override  
  369.         public void run() {  
  370.             HttpURLConnection connection = null;  
  371.             RandomAccessFile randomAccessFile = null;  
  372.             InputStream is = null;  
  373.             try {  
  374.                 URL url = new URL(urlstr);  
  375.                 connection = (HttpURLConnection) url.openConnection();  
  376.                 connection.setConnectTimeout(5000);  
  377.                 connection.setReadTimeout(8000);  
  378.                 connection.setRequestMethod("GET");  
  379.                 if (mThreadCount > 1) {// 多线程下载  
  380.                     // 设置范围,格式为Range:bytes x-y;  
  381.                     connection.setRequestProperty("Range""bytes="  
  382.                             + (startPos + compeleteSize) + "-" + endPos);  
  383.                 }  
  384.   
  385.                 randomAccessFile = new RandomAccessFile(mBean.savePath, "rwd");  
  386.                 randomAccessFile.seek(startPos + compeleteSize);  
  387.                 // 将要下载的文件写到保存在保存路径下的文件中  
  388.                 is = connection.getInputStream();  
  389.                 byte[] buffer = new byte[bufferSize];  
  390.                 int length = -1;  
  391.                 EngineUtil eUtil = EngineUtil.getInstance();  
  392.   
  393.                 if (EngineVariable.SUPPORT_NETWORK_TYPE == EngineConstants.DOWNLOAD_NETWORK_ONLYWIFI) {// 如果只能是3G下载  
  394.                     if (eUtil.getNetworkType() != EngineConstants.NETWORK_STATE_WIFI) {// 且当前网络不是Wifi  
  395.                         interruptDownloader();  
  396.                         return;  
  397.                     }  
  398.                 }  
  399.   
  400.                 while ((length = is.read(buffer)) != -1) {  
  401.                     // 网络判断  
  402.                     if (EngineVariable.SUPPORT_NETWORK_TYPE == EngineConstants.DOWNLOAD_NETWORK_ONLYWIFI) {// 如果只能是3G下载  
  403.                         if (eUtil.getNetworkType() != EngineConstants.NETWORK_STATE_WIFI) {// 且当前网络不是Wifi  
  404.                             interruptDownloader();  
  405.                             return;  
  406.                         }  
  407.                     }  
  408.   
  409.                     randomAccessFile.write(buffer, 0, length);  
  410.                     compeleteSize += length;  
  411.                     synchronized (lock_refresh_progress) {  
  412.                         mBean.currentPosition += length;  
  413.                     }  
  414.                     // 更新数据库中的下载信息  
  415.                     mDBOper.updateTaskCompleteSize(threadId, compeleteSize,  
  416.                             urlstr);  
  417.                     if (mState == EngineConstants.DOWNLOAD_STATE_PAUSE  
  418.                             || mState == EngineConstants.DOWNLOAD_STATE_INTERRUPT  
  419.                             || mState == EngineConstants.DOWNLOAD_STATE_STOP  
  420.                             || mState == EngineConstants.DOWNLOAD_STATE_ERROR) {// 暂停  
  421.                         return;  
  422.                     }  
  423.                 }  
  424.   
  425.                 // 该子线程下载完成  
  426.                 mDoneThreadCount++;  
  427.             } catch (Exception e) {  
  428.                 Log.e(TAG, "下载途中断掉了连接...");  
  429.                 interruptDownloader();  
  430.                 e.printStackTrace();  
  431.             } finally {  
  432.                 try {  
  433.                     is.close();  
  434.                     randomAccessFile.close();  
  435.                     connection.disconnect();  
  436.                 } catch (Exception e) {  
  437.                     e.printStackTrace();  
  438.                 }  
  439.             }  
  440.   
  441.             if (mDoneThreadCount == mThreadCount) {  
  442.                 downloaderDone();  
  443.             }  
  444.         }  
  445.     }  
  446.   
  447.     /** 
  448.      * 获取下载进度 
  449.      *  
  450.      * @return 
  451.      */  
  452.     public int getProgress() {  
  453.         if (mBean.fileSize < 1) {  
  454.             return 0;  
  455.         }  
  456.         return (int) (mBean.currentPosition * 100 / mBean.fileSize);  
  457.     }  
  458.   
  459.     /** 
  460.      * 暂停下载 
  461.      */  
  462.     public void pauseDownloader() {  
  463.         mState = EngineConstants.DOWNLOAD_STATE_PAUSE;  
  464.         mDBOper.addPauseFile(mBean.url, mBean.packageName, mBean.fileId);  
  465.     }  
  466.   
  467.     /** 
  468.      * 中断下载(非人为的暂停) 
  469.      */  
  470.     private void interruptDownloader() {  
  471.         mState = EngineConstants.DOWNLOAD_STATE_INTERRUPT;  
  472.     }  
  473.   
  474.     /** 
  475.      * 结束下载 
  476.      */  
  477.     public void stopDownloader() {  
  478.         mState = EngineConstants.DOWNLOAD_STATE_STOP;  
  479.         mBean.currentPosition = 0;  
  480.         removeDownloaderInfo(mBean.url);  
  481.     }  
  482.   
  483.     /** 
  484.      * 清除下载的信息 
  485.      *  
  486.      * @param urlstr 
  487.      */  
  488.     private void removeDownloaderInfo(String urlstr) {  
  489.         mDBOper.deleteDownloadTaskByUrl(urlstr);  
  490.         mDBOper.removePauseFileByUrl(urlstr);  
  491.         mBeans.clear();  
  492.     }  
  493.   
  494.     /** 
  495.      * 下载完成 
  496.      */  
  497.     private void downloaderDone() {  
  498.         mState = EngineConstants.DOWNLOAD_STATE_DONE;  
  499.         mBean.doneTime = System.currentTimeMillis();  
  500.         mCallback.callbackWhenDownloadTaskListener(mState, mBean,  
  501.                 mBean.fileName + "下载完成");  
  502.   
  503.         removeDownloaderInfo(mBean.url);  
  504.         mDBOper.addCompleteTask(mBean);// 将完成信息保存至数据库  
  505.     }  
  506.   
  507.     /** 
  508.      * 出现错误时候回调 
  509.      *  
  510.      * @param info 
  511.      */  
  512.     private void callBackError(String info) {  
  513.         mState = EngineConstants.DOWNLOAD_STATE_ERROR;  
  514.         mCallback.callbackWhenDownloadTaskListener(mState, mBean, info);  
  515.         removeDownloaderInfo(mBean.url);  
  516.     }  
  517.   
  518.     /** 
  519.      * 判断SD卡上是否留有足够的空间 
  520.      */  
  521.     private boolean hasSpaceInSDCard() {  
  522.         if (mBean.fileSize > EngineUtil.getInstance().getFreeSpaceAtDirectory(  
  523.                 Environment.getExternalStorageDirectory().getAbsolutePath())) {  
  524.             callBackError("存储卡空间不够");  
  525.             return false;  
  526.         }  
  527.         return true;  
  528.     }  
  529. }  

 

欢迎吐槽,欢迎提供建议,欢迎提供改进方法。

整个架构详见源码,绝对值得你下载收藏!这一切都是免费的,对,你不要怀疑,商业级别的源码都是免费的。
如果你觉得好,请帮忙下载源码下载

源码:http://download.csdn.net/detail/h3c4lenovo/5987789

目录
相关文章
|
7月前
|
存储 Java UED
Java网络编程:实现文件断点续传功能
文件下载是网络应用程序中的常见任务,而断点续传是提供更好用户体验的重要功能之一。本文将详细介绍如何使用Java实现文件断点续传功能,使用户能够在下载中断后从上次中断的地方继续下载。
158 0
|
4月前
|
IDE C# 开发工具
C# | 多线程批量下载文件(创建N个线程同时批量下载文件,只需要几行代码而已)
批量下载文件时使用多线程可以有效缩短完成时间,本文将讲解如何使用C#+CodePlus扩展库快速完成多线程的文件下载。 大部分代码由IDE自动生成,需要我们自己编写的代码正好**10行**。也就是说,只需要10分钟,就可以手撸一个多线程的批量下载器。
85 0
C# | 多线程批量下载文件(创建N个线程同时批量下载文件,只需要几行代码而已)
|
7月前
|
存储 监控 网络协议
Java网络编程:文件下载详解
文件下载是网络应用程序中的一个常见任务,允许用户从远程服务器获取文件。Java提供了丰富的网络编程库,使文件下载变得相对简单。本文将详细介绍如何使用Java进行文件下载,并提供一些相关内容的解释。
217 0
|
缓存 C# 图形学
C#多线程下载、断点续传的实现
做Unity热更功能的时候,发现单线程下载大尺寸资源文件的效率太低,专门去研究了下多线程下载,这里记录下相关知识点。
多线程断点下载文件
package cn.itcast.download; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; public class MulThreadDownloa
1375 0