精华内容
下载资源
问答
  • Java多线程下载文件

    万次阅读 2017-08-03 10:03:08
    Java多线程下载文件优化:合理利用服务器资源,将资源利用最大化,加快下载速度一般有两种方式: 线程池里面有N个线程,多线程下载单个文件,将网络路径的文件流切割成多快,每个线程下载一小部分,然后写入到文件...

    Java多线程下载文件

    优化:合理利用服务器资源,将资源利用最大化,加快下载速度

    一般有两种方式:

    • 线程池里面有N个线程,多线程下载单个文件,将网络路径的文件流切割成多快,每个线程下载一小部分,然后写入到文件里面,组成一个文件
    • 当有很多个文件需要下载的时候,调用某个方法,有个线程池,线程池大小假定是10,当有10个文件过来的时候,每个线程去下载一个文件即可

    多线程如果只知道Thread和Runnable,那你就Out了,Java1.5以后有个concurrent包,就是Java并发编程。

    N个线程下载单个文件

    定义一个单个线程下载的部分

    public class DownloadWithRange implements Runnable {
        private String urlLocation;
    
        private String filePath;
    
        private long start;
    
        private long end;
    
        DownloadWithRange(String urlLocation, String filePath, long start, long end) {
            this.urlLocation = urlLocation;
            this.filePath = filePath;
            this.start = start;
            this.end = end;
        }
    
        @Override
        public void run() {
            try {
                HttpURLConnection conn = getHttp();
                conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
    
                File file = new File(filePath);
                RandomAccessFile out = null;
                if (file != null) {
                    out = new RandomAccessFile(file, "rw");
                }
                out.seek(start);
                InputStream in = conn.getInputStream();
                byte[] b = new byte[1024];
                int len = 0;
                while ((len = in.read(b)) >= 0) {
                    out.write(b, 0, len);
                }
                in.close();
                out.close();
            } catch (Exception e) {
                e.getMessage();
            }
    
        }
    
        public HttpURLConnection getHttp() throws IOException {
            URL url = null;
            if (urlLocation != null) {
                url = new URL(urlLocation);
            }
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(5000);
            conn.setRequestMethod("GET");
    
            return conn;
        }
    
    }
    

    定义线程Pool

    public class DownloadFileWithThreadPool {
    
        public void getFileWithThreadPool(String urlLocation, String filePath, int poolLength) throws IOException {
            ExecutorService threadPool = Executors.newCachedThreadPool();
    
            long len = getContentLength(urlLocation);
            System.out.println(len);
            for (int i = 0; i < poolLength; i++) {
                long start = i * len / poolLength;
                long end = (i + 1) * len / poolLength - 1;
                if (i == poolLength - 1) {
                    end = len;
                }
                System.out.println(start+"---------------"+end);
                DownloadWithRange download = new DownloadWithRange(urlLocation, filePath, start, end);
                threadPool.execute(download);
            }
            threadPool.shutdown();
        }
    
        public static long getContentLength(String urlLocation) throws IOException {
            URL url = null;
            if (urlLocation != null) {
                url = new URL(urlLocation);
            }
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(5000);
            conn.setRequestMethod("GET");
            long len = conn.getContentLength();
    
            return len;
        }
    
    }
    

    测试下载单个文件

    public static void main(String[] args) {
            Date startDate = new Date();
            DownloadFileWithThreadPool pool = new DownloadFileWithThreadPool();
            try {
                pool.getFileWithThreadPool("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3", "D:\\1.mp3", 100);
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println(new Date().getTime() - startDate.getTime());
        }

    测试发现,我目前的网速,下载一个3.6M左右mp3音频文件,使用多线程,下载大概需要800ms左右,而不使用多线程,则需要1600ms以上,有时候甚至3000ms

    多文件下载,单文件单线程

    /**
     * @author Nick 带线程池的文件下载类,线程大小10
     *      文件数少的情况下,体现不大
     * @version V1.0.0
     * @Date 2017/8/2 20:43
     */
    public class FileDownConnManager {
    
        private static final Logger logger = LoggerFactory.getLogger(FileDownConnManager.class);
    
        private static final FileDownConnManager connManager = new FileDownConnManager();
    
        private static ExecutorService executorService = Executors.newFixedThreadPool(10); //10个线程跑
    
        public static FileDownConnManager getDefaultManager() {
            return connManager;
        }
    
        public static byte[] fileDown(final String netURL) throws ExecutionException, InterruptedException {
            Future<byte[]> future = executorService.submit(new Callable<byte[]>() {
                @Override
                public byte[] call() throws Exception {
                    Date date = new Date();
                    URL url;
                    byte[] getData = new byte[0];
                    InputStream is = null;
                    try {
                        url = new URL(netURL);
                        URLConnection connection = url.openConnection();
                        is = connection.getInputStream();
                        getData = readInputStream(is);
    
                    } catch (IOException e) {
                        logger.error("从URL获得字节流数组失败 " + ExceptionUtils.getMessage(e));
                    } finally {
                        try {
                            is.close();
                        } catch (IOException e) {
                            logger.error("从URL获得字节流数组流释放失败 " + ExceptionUtils.getMessage(e));
                        }
                    }
                    return getData;
                }
            });
            return future.get();
        }
    
    }

    测试

    @Test
        public void test2() throws ExecutionException, InterruptedException, IOException {
            long time1 = System.currentTimeMillis();
            for(int i = 0; i < 15; i++) {
                byte[] by1 = FileDownConnManager.fileDown("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3");
                FileUtils.writeByteArrayToFile(new File("D:\\test2_"+i+".mp3"), by1);
            }
            System.out.println(System.currentTimeMillis() - time1);
        }
    
        @Test
        public void test3() throws IOException {
            long time1 = System.currentTimeMillis();
            for(int i = 0; i < 15; i++) {
                byte[] by1 = FileFromUrlUtil.getInputStreamFromUrl("http://mpge.5nd.com/2016/2016-11-15/74847/1.mp3");
                FileUtils.writeByteArrayToFile(new File("D:\\test3_"+i+".mp3"), by1);
            }
            System.out.println(System.currentTimeMillis() - time1);
        }

    结论:同样的文件,测试发现,在一定数量(文件数量大概15)的文件下载的时候,每个文件是3.6M的情况下,使用多线程大概时间是30xxxms,而不实用多线程大概是38xxxms,还是有一定时间上的提升

    此处的测试还是会存在一定的误差,因为ExecutorService线程池的创建是需要时间,类的初始化也是需要一定的时间,假如在Web应用中,第一次创建出来了,第二次不需要创建,优势还是稍微能够体现出来!!

    展开全文
  • Java多线程文件

    千次阅读 2018-06-19 14:51:53
    调用多个线程将多个文件进行合并,当初没有考虑到多线程操作文件会造成的脏数据,导致了业务出问题。 解决方法 审查文件操作的方法,进行文件加锁,同一时间只能一个线程对文件进行操作。 代码展示 public ...

    问题场景

    调用多个线程将多个文件进行合并,当初没有考虑到多线程操作文件会造成的脏数据,导致了业务出问题。

    解决方法

    审查文件操作的方法,进行文件加锁,同一时间只能一个线程对文件进行操作。

    代码展示

    public static void copyFile(String srcFilePath, String destFilePath)
                throws IOException {
            FileInputStream in = null;
            FileOutputStream out = null;
            FileLock flout = null;
            FileChannel fcIn = null;
            FileChannel fcOut = null;
            try { // 获取源文件和目标文件的输入输出流
                in = new FileInputStream(srcFilePath);
                out = new FileOutputStream(destFilePath,true);
                // 获取输入输出通道
                fcIn = in.getChannel();
                fcOut = out.getChannel();
                while (true) {
                    try {
                        flout = fcOut.lock();
                        break;
                    } catch (OverlappingFileLockException e) {
                        Thread.sleep(1 * 10);
                    }
                }
    
                ByteBuffer buffer = ByteBuffer.allocate(4096);
                while (true) { // clear方法重设缓冲区,使它可以接受读入的数据
                    buffer.clear(); // 从输入通道中将数据读到缓冲区
                    int r = fcIn.read(buffer);
                    if (r == -1) {
                        break;
                    }
                    // flip方法让缓冲区可以将新读入的数据写入另一个通道
                    buffer.flip();
                    fcOut.write(buffer);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                flout.release();
                if (in != null && out != null) {
                    try {
                        in.close();
                        out.close();
                        System.out.println("关闭");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    展开全文
  • python 多线程写入文件

    万次阅读 2018-04-27 17:04:24
    python 多线程写入文件 在python中,对文件的操作时很简洁的,一般是通过打开文件,获取文件对象,然后对文件对象进行写入。 这是file 的一些常用方法: class file(object): def close(self): # real ...

    python 多线程写入文件

    在python中,对文件的操作时很简洁的,一般是通过打开文件,获取文件对象,然后对文件对象进行写入。

    这是file 的一些常用方法:

    class file(object):
       
        def close(self): # real signature unknown; restored from __doc__
            
            pass
    
        def fileno(self): # real signature unknown; restored from __doc__
           
            return 0
    
        def flush(self): # real signature unknown; restored from __doc__
            
            pass
    
        def isatty(self): # real signature unknown; restored from __doc__
            
            return False
    
        def next(self): # real signature unknown; restored from __doc__
            
            pass
    
        def read(self, size=None): # real signature unknown; restored from __doc__
           
            pass
    
        def readinto(self): # real signature unknown; restored from __doc__
           
            pass
    
        def readline(self, size=None): # real signature unknown; restored from __doc__
           
            pass
    
        def readlines(self, size=None): # real signature unknown; restored from __doc__
           
            return []
    
        def seek(self, offset, whence=None): # real signature unknown; restored from __doc__
           
            pass
    
        def tell(self): # real signature unknown; restored from __doc__
            """ tell() -> current file position, an integer (may be a long integer). """
            pass
    
        def truncate(self, size=None): # real signature unknown; restored from __doc__
           
            pass
    
        def write(self, p_str): # real signature unknown; restored from __doc__
            
            pass
    
        def writelines(self, sequence_of_strings): # real signature unknown; restored from __doc__
           
            pass
    
        def xreadlines(self): # real signature unknown; restored from __doc__
           
            pass

    通常是通过write 直接一个挨着一个的写,如果有一天,突然想要多个线程去写一个文件,怎们办呢??

    思路是这样的:

        1.对于文件的大小要确定

        2.并且对于每个线程的需要处理的内容大小进行划分

        3.然后在各个线程里面将内容写入进去

        4.需要注意的是对于每个线程,要给予一个文件对象,通常是在主线程中打开文件获得文件对象,然后复制这个文件对象的描述符,并用os.fdopen()获取这个文件对象,传递给各个线程使用!


    另外一种方式:

    如果对文件的大小,事先是不知道的,仅仅可以参考里面有多少行,具体的大小,不知道的时候怎们办呢。

    解决方法可以是:按行进行分批,每个线程下载固定的行数,到不同的缓存文件中,然后对文件进行拼接获取完整文件!此时可以根据线程数量进行多倍效率的提高!

    注意:在进行一些文件操作的时候,过多的打印一些输出到控制台会影响程序的效率,一定注意!

     

    展开全文
  • 多线程下载文件(支持暂停、取消、断点续

    万次阅读 多人点赞 2017-02-23 15:31:09
    多线程下载文件(支持暂停、取消、断点续多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每...

    多线程下载文件(支持暂停、取消、断点续传)

    多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每一个线程下载的部分组装起来即可。

    涉及的知识及问题

    • 请求的数据如何分段
    • 分段完成后如何下载和下载完成后如何组装到一起
    • 暂停下载和继续下载的实现(wait()、notifyAll()、synchronized的使用)
    • 取消下载和断点续传的实现

    一、请求的数据如何分段

    首先通过HttpURLConnection请求总文件大小,而后根据线程数计算每一个线程的下载量,在分配给每一个线程去下载

    fileLength = conn.getContentLength();
    //根据文件大小,先创建一个空文件
    //“r“——以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
    //“rw“——打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
    //“rws“—— 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
    //“rwd“——打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。
    RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");
    raf.setLength(fileLength);
    raf.close();
    //计算各个线程下载的数据段
    int blockLength = fileLength / threadCount;

    二、分段完成后如何下载和下载完成后如何组装到一起

    分段完成后给每一个线程的请求头设置Range参数,他允许客户端只请求文件的一部分数据,每一个线程只请求下载相应范围内的数据,使用RandomAccessFile(可随机读写的文件)写入到同一个文件里即可组装成目标文件Range,是在 HTTP/1.1里新增的一个 header field,它允许客户端实际上只请求文档的一部分(范围可以相互重叠)

    Range的使用形式:

    属性 解释
    bytes=0-499 表示头500个字节
    bytes=500-999 表示第二个500字节
    bytes=-500 表示最后500个字节
    bytes=500- 表示500字节以后的范围
    bytes=0-0,-1 第一个和最后一个字节

    HttpUrlConnection中设置请求头

    URL url = new URL(loadUrl);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
    conn.setConnectTimeout(5000);
    //若请求头加上Range这个参数,则返回状态码为206,而不是200
    if (conn.getResponseCode() == 206) {
        InputStream is = conn.getInputStream();
        RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");
        raf.seek(startPosition);//跳到指定位置开始写数据
    }

    三、暂停下载和继续下载的实现(wait()、notifyAll()、synchronized的使用)

    关于synchronized只需记住一下五点:

    1. 当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
    2. 然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
    3. 尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
    4. 第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
    5. 以上规则对其它对象锁同样适用.
        protected void onPause() {
            if (mThreads != null)
                stateDownload = DOWNLOAD_PAUSE;
        }
        protected void onStart() {
            if (mThreads != null)
                synchronized (DOWNLOAD_PAUSE) {
                    stateDownload = DOWNLOAD_ING;
                    DOWNLOAD_PAUSE.notifyAll();
                }
        }

    对于wait()、notify()、notifyAll()需要注意的是

    1. 调用任何对象的wait()方法时,都必须先获得该对象的锁,即调用的wait()方法必须得写在synchronized(obj){…}之内
    2. 当调用对象的wait()方法后,该线程若想继续执行,必须得再次获得该对象的锁才可以
    3. 如果A1,A2,A3线程都在obj.wait(),则B调用object.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)
    4. 当B调用object.notify/notifyAll的时候,B正持有object锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得object锁直到B退出synchronized块,释放object锁后,A1,A2,A3中的一个/全部才有机会获得锁继续执行
        synchronized (DOWNLOAD_PAUSE) {
            if (stateDownload.equals(DOWNLOAD_PAUSE)) {
                DOWNLOAD_PAUSE.wait();
            }
        }

    四、取消下载和断点续传的实现

    取消下载即取消每个线程的执行,不建议直接使用Thread.stop()方法,安全的取消线程即run方法执行结束。只要控制住循环,就可以让run方法结束,也就是线程结束

        while ((len = is.read(buffer)) != -1) {
            //是否继续下载
            if (!isGoOn)
                break;
        }

    断点续传即其实和重新下载是一样的,不过文件的大小和每一个线程下载时的起始位置和结束位置都不是重新计算的。而是上次取消下载时,每一个线程保存的当前位置和结束位置,让每一个线程接着上次的地方继续下载即可

        SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        //获取上次取消下载的进度,若没有则返回0
        currLength = sp.getInt(CURR_LENGTH, 0);
        for (int i = 0; i < threadCount; i++) {
            //开始位置,获取上次取消下载的进度,默认返回i*blockLength,即第i个线程开始下载的位置
            int startPosition = sp.getInt(SP_NAME + (i + 1), i * blockLength);
            //结束位置,-1是为了防止上一个线程和下一个线程重复下载衔接处数据
            int endPosition = (i + 1) * blockLength - 1;
            //将最后一个线程结束位置扩大,防止文件下载不完全,大了不影响,小了文件失效
            if ((i + 1) == threadCount)
            endPosition = endPosition * 2;
            mThreads[i] = new DownThread(i + 1, startPosition, endPosition);
            mThreads[i].start();
        }

    网络获取和读写SD卡都需要添加相应权限

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    下面贴上全部的代码,里面有详细的注释DownLoadFile.java

    import android.content.Context;
    import android.content.SharedPreferences;
    import android.os.Handler;
    import android.os.Message;
    
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    /**
     * Created by tianzhao on 2017/2/21 09:25.
     * 多线程下载文件
     */
    
    public class DownLoadFile {
    
    
        private static final String SP_NAME = "download_file";
        private static final String CURR_LENGTH = "curr_length";
        private static final int DEFAULT_THREAD_COUNT = 4;//默认下载线程数
        //以下为线程状态
        private static final String DOWNLOAD_INIT = "1";
        private static final String DOWNLOAD_ING = "2";
        private static final String DOWNLOAD_PAUSE = "3";
    
        private Context mContext;
    
        private String loadUrl;//网络获取的url
        private String filePath;//下载到本地的path
        private int threadCount = DEFAULT_THREAD_COUNT;//下载线程数
    
        private int fileLength;//文件总大小
        //使用volatile防止多线程不安全
        private volatile int currLength;//当前总共下载的大小
        private volatile int runningThreadCount;//正在运行的线程数
        private Thread[] mThreads;
        private String stateDownload = DOWNLOAD_INIT;//当前线程状态
    
        private DownLoadListener mDownLoadListener;
    
        public void setOnDownLoadListener(DownLoadListener mDownLoadListener) {
            this.mDownLoadListener = mDownLoadListener;
        }
    
        interface DownLoadListener {
            //返回当前下载进度的百分比
            void getProgress(int progress);
    
            void onComplete();
    
            void onFailure();
        }
    
        public DownLoadFile(Context mContext, String loadUrl, String filePath) {
            this(mContext, loadUrl, filePath, DEFAULT_THREAD_COUNT, null);
        }
    
        public DownLoadFile(Context mContext, String loadUrl, String filePath, DownLoadListener mDownLoadListener) {
            this(mContext, loadUrl, filePath, DEFAULT_THREAD_COUNT, mDownLoadListener);
        }
    
        public DownLoadFile(Context mContext, String loadUrl, String filePath, int threadCount) {
            this(mContext, loadUrl, filePath, threadCount, null);
        }
    
        public DownLoadFile(Context mContext, String loadUrl, String filePath, int threadCount, DownLoadListener mDownLoadListener) {
            this.mContext = mContext;
            this.loadUrl = loadUrl;
            this.filePath = filePath;
            this.threadCount = threadCount;
            runningThreadCount = 0;
            this.mDownLoadListener = mDownLoadListener;
        }
    
        /**
         * 开始下载
         */
        protected void downLoad() {
            //在线程中运行,防止anr
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //初始化数据
                        if (mThreads == null)
                            mThreads = new Thread[threadCount];
    
                        //建立连接请求
                        URL url = new URL(loadUrl);
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                        conn.setConnectTimeout(5000);
                        conn.setRequestMethod("GET");
                        int code = conn.getResponseCode();//获取返回码
                        if (code == 200) {//请求成功,根据文件大小开始分多线程下载
                            fileLength = conn.getContentLength();
                            //根据文件大小,先创建一个空文件
                            //“r“——以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
                            //“rw“——打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
                            //“rws“—— 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
                            //“rwd“——打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。
                            RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");
                            raf.setLength(fileLength);
                            raf.close();
                            //计算各个线程下载的数据段
                            int blockLength = fileLength / threadCount;
    
                            SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
                            //获取上次取消下载的进度,若没有则返回0
                            currLength = sp.getInt(CURR_LENGTH, 0);
                            for (int i = 0; i < threadCount; i++) {
                                //开始位置,获取上次取消下载的进度,默认返回i*blockLength,即第i个线程开始下载的位置
                                int startPosition = sp.getInt(SP_NAME + (i + 1), i * blockLength);
                                //结束位置,-1是为了防止上一个线程和下一个线程重复下载衔接处数据
                                int endPosition = (i + 1) * blockLength - 1;
                                //将最后一个线程结束位置扩大,防止文件下载不完全,大了不影响,小了文件失效
                                if ((i + 1) == threadCount)
                                    endPosition = endPosition * 2;
    
                                mThreads[i] = new DownThread(i + 1, startPosition, endPosition);
                                mThreads[i].start();
                            }
                        } else {
                            handler.sendEmptyMessage(FAILURE);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        handler.sendEmptyMessage(FAILURE);
                    }
                }
            }).start();
        }
    
        /**
         * 取消下载
         */
        protected void cancel() {
            if (mThreads != null) {
                //若线程处于等待状态,则while循环处于阻塞状态,无法跳出循环,必须先唤醒线程,才能执行取消任务
                if (stateDownload.equals(DOWNLOAD_PAUSE))
                    onStart();
                for (Thread dt : mThreads) {
                    ((DownThread) dt).cancel();
                }
            }
        }
    
        /**
         * 暂停下载
         */
        protected void onPause() {
            if (mThreads != null)
                stateDownload = DOWNLOAD_PAUSE;
        }
    
        /**
         * 继续下载
         */
        protected void onStart() {
            if (mThreads != null)
                synchronized (DOWNLOAD_PAUSE) {
                    stateDownload = DOWNLOAD_ING;
                    DOWNLOAD_PAUSE.notifyAll();
                }
        }
    
        protected void onDestroy() {
            if (mThreads != null)
                mThreads = null;
        }
    
        private class DownThread extends Thread {
    
            private boolean isGoOn = true;//是否继续下载
            private int threadId;
            private int startPosition;//开始下载点
            private int endPosition;//结束下载点
            private int currPosition;//当前线程的下载进度
    
            private DownThread(int threadId, int startPosition, int endPosition) {
                this.threadId = threadId;
                this.startPosition = startPosition;
                currPosition = startPosition;
                this.endPosition = endPosition;
                runningThreadCount++;
            }
    
            @Override
            public void run() {
                SharedPreferences sp = mContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
                try {
                    URL url = new URL(loadUrl);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
                    conn.setConnectTimeout(5000);
                    //若请求头加上Range这个参数,则返回状态码为206,而不是200
                    if (conn.getResponseCode() == 206) {
                        InputStream is = conn.getInputStream();
                        RandomAccessFile raf = new RandomAccessFile(filePath, "rwd");
                        raf.seek(startPosition);//跳到指定位置开始写数据
                        int len;
                        byte[] buffer = new byte[1024];
                        while ((len = is.read(buffer)) != -1) {
                            //是否继续下载
                            if (!isGoOn)
                                break;
                            //回调当前进度
                            if (mDownLoadListener != null) {
                                currLength += len;
                                int progress = (int) ((float) currLength / (float) fileLength * 100);
                                handler.sendEmptyMessage(progress);
                            }
    
                            raf.write(buffer, 0, len);
                            //写完后将当前指针后移,为取消下载时保存当前进度做准备
                            currPosition += len;
                            synchronized (DOWNLOAD_PAUSE) {
                                if (stateDownload.equals(DOWNLOAD_PAUSE)) {
                                    DOWNLOAD_PAUSE.wait();
                                }
                            }
                        }
                        is.close();
                        raf.close();
                        //线程计数器-1
                        runningThreadCount--;
                        //若取消下载,则直接返回
                        if (!isGoOn) {
                            //此处采用SharedPreferences保存每个线程的当前进度,和三个线程的总下载进度
                            if (currPosition < endPosition) {
                                sp.edit().putInt(SP_NAME + threadId, currPosition).apply();
                                sp.edit().putInt(CURR_LENGTH, currLength).apply();
                            }
                            return;
                        }
                        if (runningThreadCount == 0) {
                            sp.edit().clear().apply();
                            handler.sendEmptyMessage(SUCCESS);
                            handler.sendEmptyMessage(100);
                            mThreads = null;
                        }
                    } else {
                        sp.edit().clear().apply();
                        handler.sendEmptyMessage(FAILURE);
                    }
                } catch (Exception e) {
                    sp.edit().clear().apply();
                    e.printStackTrace();
                    handler.sendEmptyMessage(FAILURE);
                }
            }
    
            public void cancel() {
                isGoOn = false;
            }
        }
    
        private final int SUCCESS = 0x00000101;
        private final int FAILURE = 0x00000102;
    
        private Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
    
                if (mDownLoadListener != null) {
                    if (msg.what == SUCCESS) {
                        mDownLoadListener.onComplete();
                    } else if (msg.what == FAILURE) {
    
                        mDownLoadListener.onFailure();
                    } else {
                        mDownLoadListener.getProgress(msg.what);
                    }
                }
            }
        };
    }

    在MainActivity中的使用

    import android.os.Bundle;
    import android.os.Environment;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity {
        DownLoadFile downLoadFile;
        private String loadUrl = "http://gdown.baidu.com/data/wisegame/d2fbbc8e64990454/wangyiyunyinle_87.apk";
        private String filePath = Environment.getExternalStorageDirectory()+"/"+"网易云音乐.apk";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            final TextView tvprogress = (TextView) findViewById(R.id.tv_progress);
            downLoadFile = new DownLoadFile(this,loadUrl, filePath, 3);
            downLoadFile.setOnDownLoadListener(new DownLoadFile.DownLoadListener() {
                @Override
                public void getProgress(int progress) {
                    tvprogress.setText("当前进度 :"+progress+" %");
                }
    
                @Override
                public void onComplete() {
                    Toast.makeText(MainActivity.this,"下载完成",Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void onFailure() {
                    Toast.makeText(MainActivity.this,"下载失败",Toast.LENGTH_SHORT).show();
                }
            });
            findViewById(R.id.bt).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    downLoadFile.downLoad();
                }
            });
            findViewById(R.id.bt_pause).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    downLoadFile.onPause();
                }
            });
            findViewById(R.id.bt_start).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    downLoadFile.onStart();
                }
            });
            findViewById(R.id.bt_cancel).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    downLoadFile.cancel();
                }
            });
        }
    
        @Override
        protected void onDestroy() {
            downLoadFile.onDestroy();
            super.onDestroy();
        }
    }
    

    关于多线程下载文件就说道这,若有不正之处,多多指教!

    展开全文
  • Aandroid 多线程断点续同时下载多个大文件Aandroid 多线程断点续同时下载多个大文件最近学习在Android环境中一些网络请求方面的知识,其中有一部分是关于网络下载方面的知识。在这里解析一下自己写的demo,总结...
  • TCP多线程实现文件上传

    千次阅读 2020-06-01 16:47:02
    TCP多线程实现文件上传例子
  • 多线程下载文件

    千次阅读 2011-08-23 20:59:09
    使用多线程下载文件可以更快完成文件的下载,多线程下载文件之所以快,是因为其抢占的服务器资源多。如:假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计算机中并非并发执行,而是...
  • 【多线程】——JAVA多线程文件下载

    千次阅读 2019-07-05 16:52:41
    在项目开发过程中,需要对服务器上的某些文件进行下载,最好还是多线程的,网查查阅一下资料,整理两个可以用的案例,特此记录。两个案例分别采用文件流的方式和RandomAccessFile实现。 一、文件流操作 Download...
  • 文件上传-队列多线程读取文件内容

    千次阅读 2016-08-22 18:25:59
    队列多线程读取文件内容 为什么采用多线程队列的方式呢?主要是如果文件一行一行读取处理的话,页面上传文件后,需要等待服务器响应。如果文件的内容数据量很大的话,页面就一直等待服务器响应,毕竟服务器处理这些...
  • c# 多线程文件批量下载

    千次阅读 2018-12-11 21:22:14
    c# 多线程文件批量下载 废话少说,先演示一张效果图     简单说下过程喽   开发过程中其实总是会碰到项目想应用下载文件~ 看其他语言有很多封装好的类库可以使用~~ 作为小白的我并没有找到很多c#的...
  • 今天遇到一个问题,需要多个...首先我们写个文件,然后多线程读取,以下是我实验的代码:package com.alibaba.middleware.race;import java.io.IOException; import java.io.RandomAccessFile;public class Test2 { pub
  • 干货,Java多线程下载文件以及断点续

    千次阅读 热门讨论 2017-12-27 16:26:11
    普通的下载文件就是用户请求服务器的文件地址,并且传输数据到用户的本地就完成了下载,多线程下载是用户向服务器请求的文件切割成了几块,每个线程负责其中的一块文件下载,这样就提高了下载的效率。当然,多线程...
  • pigz多线程压缩文件

    万次阅读 2016-09-13 11:35:20
    pigz是linux下多线程压缩软件,发音: “pig-zee”。 使用方法: tar cf - mydir | pigz >mydir.tgz 如果文件少可以: tar cvf - mydir | pigz >mydir.tgz 加v参数,显示压缩的文件列表。文件多显示会比较费...
  • 多线程拷贝文件

    千次阅读 2008-07-28 22:48:00
    看过请留个言,转载请注明出处,尊重作者劳动成果,谢谢! 以下是一个用多线程拷贝大文件的例子,供大家参考:... /** * @author leno * @version V1.0 利用多线程拷贝文件的类 */public class MultiThreadCopy
  • Java带进度多线程下载文件

    千次阅读 2016-02-18 15:48:11
    前面讲了基本的IO,但是实际的项目中,文件大了之后一般会使用多线程来进行处理。本文主要是使用多线程文件进行下载,并提供了进度显示。在实现的过程中遇到了很多小问题,都是因为知识点不熟,查阅了官方文档之后...
  • 要实现的是开多线程传输一个文件,目的是提高传输速度。 思路是这样的:服务端开多线程,将文件分割,一个线程传输一块,客户端也开多线程,每个线程接受一块,最后拼成一个完整的文件。服务端支持多线程, 客户端要...
  • 多线程文件操作

    千次阅读 2017-06-20 18:14:04
    最近项目有个需求,对数据库中的号码进行检查,检查号码状态是否正常,检查方法需要依赖接口,每个号码调一次http接口, 基本思路: 1,轮询分页查询数据库,取号码 ...后边就使用了多线程处理,网上查了下,基本都是
  • 多线程实现文件拷贝

    千次阅读 2018-06-05 15:41:41
    关于多线程,大家可以看下这个博客...下面直接贴我自己写的多线程实现文件拷贝的代码。package util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; ...
  • (1) 多线程文件下载的总体思路是:把一个下载任务分配给多个线程,首先获取文件的长度,平分给多个线程,每个线程负责指定的文件块,待全部线程完成后,再把各个线程下载的临时文件合并。
  • C#多线程读取文件

    千次阅读 2014-12-02 20:08:16
    指定目录中有若干个很小的文本文件,现在需要使用多线程进行读取。 使用 backgroundworker 或者 delegate 委托的方式实现,下面是使用委托的线程安全模式, textBox1是你的编辑框: ------------------------------...
  • python 多线程文件操作

    千次阅读 2018-08-02 16:20:34
    使用python 将在csv文件中的一百万条网址,写入mongo数据库中,这里使用多线程进行操作。直接贴上代码,如下: import os import threading #导入进程 import csv import time from Mongo_cache import ...
  • 每写入一次文件就将这次写入文件的位置保存起来(保存到文件中),下次读取文件的的位置开始下载public class MuchThreadDown { private static int threadCount = 3;// 开启3个线程 private static int block...
  • java多线程读写文件文件

    千次阅读 2014-05-22 21:46:06
    文件修改始终是一件很麻烦也很出错d
  • qt-多线程文件

    千次阅读 2016-05-27 14:59:23
    qt本身提供了许多多线程的类,方便我们进行多线程的开发创建线程:线程类Qt 包含下面一些线程相关的类: QThread 提供了开始一个新线程的方法 QThreadStorage 提供逐线程数据存储 QMutex 提供相互排斥的锁,或互斥量...
  • 多线程读写文件实现方法

    千次阅读 2018-08-02 15:03:45
    //并发运行写入日志记录,由于线程同时写入同一个文件将会导致错误 Parallel.For(0,LogCount,e => { WriteLog0(); WriteLog1(); }); Console.WriteLine(string.Format("\r\n第一个文件 Log Count:{0}.\t\...
  • java多线程读写文件实例

    万次阅读 2013-12-16 22:39:45
    Java多线程文件: package com.myjava; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import ...
  • java 多线程下载文件

    万次阅读 2015-04-08 22:55:08
    //多线程下载先要生成一个大小一样的文件,在进行写入; RandomAccessFile accessFile = new RandomAccessFile(file, "rwd"); accessFile.setLength(length); accessFile.close(); int block = length%...
  • Java多线程下载文件实例

    千次阅读 2018-09-04 10:07:52
    //计算每个线程请求文件的开始和结束位置 long pos = 0; long endPos = pos + packageLength; for(int i=0; i; i++){ if(leftLength >0){ endPos ++; leftLength--; } service.execute(new DownLoadThread...
  • Golang多线程文件传输

    千次阅读 2013-11-08 22:08:33
    Golang多线程文件传输 ------------------------------ 前段时间抽空用Golang简单实现了一下多线程文件传输。 采用的方式为将待传输的文件拆分成多部分由多个协程同时传输。服务端在全部数据接收完成后将文件拼接...
  • 在QT中采用多线程下载文件

    千次阅读 2017-01-19 15:36:12
    在QT中采用多线程下载文件这里的线程是指下载的通道(和操作系统中的线程不一样),一个线程就是一个文件的下载通道,多线程也就是同时开起好几个下载通道.当服务器提供下载服务 时,使用下载者是共享带宽的,在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,078,484
精华内容 431,393
关键字:

多线程传文件