java文件下载,支持任务暂停,恢复,断点续传;任务状态查询;任务并发控制(代码片段)

author author     2022-12-25     254

关键词:

package everphoto.bean;

import android.content.Context;
import android.support.v4.util.ArrayMap;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import everphoto.App;
import solid.download.DownloadTask;
import solid.download.DownloadTaskListener;
import solid.util.PathUtils;

/**
 * 管理应用所有下载任务,提供以下功能
 * 1.任务状态查询
 * 2.任务并发控制
 * 3.任务暂停、恢复、断点续传
 */
public class FileDownloadMgr 
    private static final int MAX_CONCURRENT_DOWNLOAD_TASK_NUM = 5;

    private static Context context;
    private static FileDownloadMgr instance = new FileDownloadMgr();

    public static FileDownloadMgr getInstance(Context c) 
        if (c != null) 
            context = c;
        
        return instance;
    

    /**
     * 必须和taskList保持一致性
     */
    private Map<String, DownloadTask> tasks = new ArrayMap<>();
    /**
     * 任务队列
     */
    private LinkedList<String> taskList = new LinkedList<>();
    private int concurrentTaskNum = 0;

    private FileDownloadMgr() 
    

    public boolean pasue(String url) 
        DownloadTask task = tasks.get(url);
        if (null == task) 
            return false;
        

        task.onCancelled();
        finish(url);
        return true;
    

    public DownloadTask getTask(String url) 
        return tasks.get(url);
    

    public boolean putTask(final String url, final DownloadTaskListener listener) 
        return putTask(url, PathUtils.PATH_DOWNLOAD, null, listener, false);
    

    public boolean putTask(final String url, String fileName, final DownloadTaskListener listener) 
        return putTask(url, PathUtils.PATH_DOWNLOAD, fileName, listener, false);
    

    public boolean putTask(final String url, String fileName, final DownloadTaskListener listener, boolean force) 
        return putTask(url, PathUtils.PATH_DOWNLOAD, fileName, listener, force);
    

    /**
     * 生成并执行一个下载任务,支持恢复一个被暂停或异常中断的下载任务。
     *
     * @param url      下载url
     * @param path     保存路径
     * @param fileName 保存文件的文件名;如果为空,则使用url.getFile()作为文件名
     * @param listener DownloadTaskListener
     * @param force    是否无视最大下载并发的限制,强制下载
     * @return 是否成功生成任务
     */
    public boolean putTask(final String url, String path, String fileName, final DownloadTaskListener listener, boolean force) 
        try 
            DownloadTask task = new DownloadTask(context, url, path, fileName, new DownloadTaskListener() 
                @Override
                public void updateProcess(DownloadTask task) 
                    listener.updateProcess(task);
                

                @Override
                public void finishDownload(DownloadTask task, File file) 
                    try 
                        listener.finishDownload(task, file);
                     catch (Exception e) 
                        e.printStackTrace();
                    
                    finish(url);
                

                @Override
                public void preDownload() 
                    listener.preDownload();
                

                @Override
                public void errorDownload(int error) 
                    listener.errorDownload(error);
                
            , App.getInstance().provideMediaImageHttpClient());

            tasks.put(url, task);
            taskList.add(url);
            execute(task, force);
            return true;
         catch (MalformedURLException e) 
            e.printStackTrace();
         catch (FileNotFoundException e) 
            e.printStackTrace();
        
        return false;
    

    /**
     * 提供一键暂停、一键恢复功能
     */
    public void showNotification() 

    

    /**
     * @param task  下载任务
     * @param force 强制执行下载,忽略最大并发限制
     */
    private synchronized void execute(DownloadTask task, boolean force) 
        if (concurrentTaskNum >= MAX_CONCURRENT_DOWNLOAD_TASK_NUM && !force) 
            return;
        
        concurrentTaskNum++;
        task.execute();
    

    private synchronized void finish(String url) 
        taskList.remove(url);
        tasks.remove(url);
        concurrentTaskNum--;

        // 找到队列最前面的未执行任务
        Iterator iterator = taskList.iterator();
        while (iterator.hasNext()) 
            String taskUrl = (String) iterator.next();
            DownloadTask task = tasks.get(taskUrl);
            if (task != null && task.getStatus() == DownloadTask.Status.PENDING) 
                execute(task, false);
            
        
    

package solid.download;

import java.io.File;

public interface DownloadTaskListener 
    void updateProcess(DownloadTask task);

    void finishDownload(DownloadTask task, File file);

    void preDownload();

    void errorDownload(int error);

package solid.download;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;

import com.squareup.okhttp.Call;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;

import solid.util.FileUtils;
import solid.util.IOUtils;
import solid.util.L;

public class DownloadTask extends AsyncTask<Void, Integer, Long> 
    public final static int ERROR_NONE = 0;
    public final static int ERROR_SD_NO_MEMORY = 1;
    public final static int ERROR_BLOCK_INTERNET = 2;
    public final static int ERROR_UNKONW = 3;
    public final static int TIME_OUT = 30000;

    private final static int BUFFER_SIZE = 1024 * 8;

    private URL url;
    private File file;
    private String urlString;
    private Throwable exception;
    private DownloadTaskListener listener;
    private Context context;
    private Call call = null;
    private OkHttpClient client;

    private long downloadSize;
    private long previousFileSize;
    private long totalSize;
    private int downloadPercent;
    private long networkSpeed; // 网速
    private long previousTime;
    private long totalTime;
    private int errStatusCode = ERROR_NONE;
    private boolean interrupt = false;

    public DownloadTask(Context context, String urlString, String path)
            throws MalformedURLException, FileNotFoundException 
        this(context, urlString, path, null, null, null);
    

    public DownloadTask(Context context, String urlString, String path, String fileName, DownloadTaskListener listener, OkHttpClient client)
            throws MalformedURLException, FileNotFoundException 
        super();
        this.context = context;
        this.urlString = urlString;
        this.url = new URL(urlString);
        this.listener = listener;
        this.client = client;

        if (TextUtils.isEmpty(fileName)) 
            fileName = new File(url.getFile()).getName();
            fileName = FileUtils.fixFileName(fileName);
        
        L.v(null, "fileName: " + fileName);

        if (!FileUtils.makeSurePath(path)) 
            throw new FileNotFoundException("makeSurePath " + path);
        

        this.file = new File(path, fileName);
    

    public DownloadTaskListener getListener() 
        return listener;
    

    public File getFile() 
        return file;
    

    public String getUrlString() 
        return urlString;
    

    public int getDownloadPercent() 
        return downloadPercent;
    

    public long getDownloadSize() 
        return downloadSize + previousFileSize;
    

    public long getTotalSize() 
        return totalSize;
    

    public long getDownloadSpeed() 
        return this.networkSpeed;
    

    public long getTotalTime() 
        return this.totalTime;
    

    @Override
    public void onCancelled() 
        super.onCancelled();
        interrupt = true;
    

    public int copy(InputStream input, RandomAccessFile out) throws Exception 
        byte[] buffer = new byte[BUFFER_SIZE];

        BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
        L.v(null, "length" + out.length());
        out.seek(out.length());

        int count = 0, n = 0;
        long errorBlockTimePreviousTime = -1, expireTime = 0;
        try 
            while (!interrupt) 
                n = in.read(buffer, 0, BUFFER_SIZE);
                if (n == -1) 
                    break;
                
                out.write(buffer, 0, n);

                count += n;
                if (!isOnline()) 
                    interrupt = true;
                    errStatusCode = ERROR_BLOCK_INTERNET;
                    break;
                

                if (networkSpeed == 0) 
                    if (errorBlockTimePreviousTime > 0) 
                        expireTime = System.currentTimeMillis() - errorBlockTimePreviousTime;
                        if (expireTime > TIME_OUT) 
                            errStatusCode = ERROR_BLOCK_INTERNET;
                            interrupt = true;
                        
                     else 
                        errorBlockTimePreviousTime = System.currentTimeMillis();
                    
                 else 
                    expireTime = 0;
                    errorBlockTimePreviousTime = -1;
                
            
         finally 
            try 
                out.close();
             catch (IOException e) 
                errStatusCode = ERROR_UNKONW;
                Log.e(null, e.getMessage(), e);
            
            try 
                in.close();
             catch (IOException e) 
                errStatusCode = ERROR_UNKONW;
                Log.e(null, e.getMessage(), e);
            
        
        return count;
    

    @Override
    protected void onPreExecute() 
        previousTime = System.currentTimeMillis();
        if (listener != null)
            listener.preDownload();
    

    @Override
    protected Long doInBackground(Void... params) 
        try 
            return download();
         catch (Exception e) 
            if (call != null) 
                call.cancel();
            
            exception = e;
            errStatusCode = ERROR_UNKONW;
            return null;
        
    

    @Override
    protected void onProgressUpdate(Integer... progress) 
        if (progress.length > 1) 
            totalSize = progress[1];
            if (totalSize == -1) 
                if (listener != null)
                    listener.errorDownload(ERROR_UNKONW);
            
         else 
            totalTime = System.currentTimeMillis() - previousTime;
            downloadSize = progress[0];
            downloadPercent = (int) ((downloadSize + previousFileSize) * 100 / totalSize);
            networkSpeed = downloadSize / totalTime;
            if (listener != null)
                listener.updateProcess(this);
        
    

    @Override
    protected void onPostExecute(Long result) 
        if (interrupt) 
            if (errStatusCode != ERROR_NONE) 
                if (listener != null)
                    listener.errorDownload(errStatusCode);
            

            return;
        

        if (exception != null) 
            Log.v(null, "Download failed.", exception);
        
        if (listener != null) 
            listener.finishDownload(this, file);
        
    

    private long download() throws Exception 
        L.v(null, "totalSize: " + totalSize);

        if (null == client) 
            client = new OkHttpClient();
        

        Request request = new Request.Builder().url(urlString).build();
        Call call = client.newCall(request);
        Response response = call.execute();
        totalSize = response.body().contentLength();

        if (file.length() > 0 && totalSize > 0 && totalSize > file.length()) 
            // 此处要求服务端支持断点续传
            request = request.newBuilder().addHeader("Range", "bytes=" + file.length() + "-").build();
            previousFileSize = file.length();
            call.cancel();
            call = client.newCall(request);
            response = call.execute();
            L.v(null, "File is not complete, download now.");
            L.v(null, "File length:" + file.length() + " totalSize:" + totalSize);
         else if (file.exists() && totalSize <= file.length()) 
            L.v(null, "Output file already exists. Skipping download.");
            publishProgress((int) totalSize);
            return 0l;
        


        long storage = FileUtils.getAvailableStorage();
        L.i(null, "storage:" + storage + " totalSize:" + totalSize);
        if (totalSize - file.length() > storage) 
            errStatusCode = ERROR_SD_NO_MEMORY;
            interrupt = true;
            call.cancel();
            return 0l;
        
        RandomAccessFile outputStream;
        try 
            outputStream = new ProgressReportingRandomAccessFile(file, "rw");
         catch (FileNotFoundException e) 
            L.v(null, "OutputStream Error");
            throw e;
        

        int bytesCopied = 0;
        try 
            publishProgress(0, (int) totalSize);

            InputStream input = null;
            input = response.body().byteStream();
//        try 
////            input = response.getEntity().getContent();
//         catch (IOException ex) 
//            errStatusCode = ERROR_UNKONW;
//            call.cancel();
//            Log.v(null, "InputStream Error" + ex.getMessage());
//            return 0;
//        

            bytesCopied = copy(input, outputStream);

            if ((previousFileSize + bytesCopied) != totalSize && totalSize != -1 && !interrupt) 
                throw new IOException("Download incomplete: " + bytesCopied + " != " + totalSize);
            

            call.cancel();
            call = null;
            L.v(null, "Download completed successfully.");
         finally 
            IOUtils.close(outputStream);
        
        return bytesCopied;
    

    private boolean isOnline() 
        try 
            ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
                    Context.CONNECTIVITY_SERVICE);
            NetworkInfo ni = cm.getActiveNetworkInfo();
            return ni != null && ni.isConnectedOrConnecting();
         catch (Exception e) 
            e.printStackTrace();
            return false;
        
    

    private final class ProgressReportingRandomAccessFile extends RandomAccessFile 
        private int progress = 0;

        public ProgressReportingRandomAccessFile(File file, String mode)
                throws FileNotFoundException 
            super(file, mode);
        

        @Override
        public void write(byte[] buffer, int offset, int count) throws IOException 
            super.write(buffer, offset, count);
            progress += count;
            publishProgress(progress);
        
    

断点续传基本原理初了解

...个部分,同时进行多个部分一起的下载,当某个时间点,任务被暂停了,此时下载暂停的位置就是断点了。续传就是当一个未完成的下载任务再次开始时,会从上次的断点继续传送。    使用多线程断点续传下载的时候,将... 查看详情

讲讲断点续传那点儿事(代码片段)

...件是否还需要重头开始下载?Q3:你的app下载大文件时,支持暂停并恢复下载么?即使这两个操作分布在程 查看详情

谷歌浏览器下载软件可以断点续传吗?

...可以,不用设置,自动的。关键问题是你所下载的服务器支持不支持。如果不支持也不行。参考技术A在浏览器不关闭的时候可以暂停,但是关闭以后就不能像迅雷一样接着下载了 参考技术B这个与用什么浏览器没关,关键是看所... 查看详情

java实现文件的断点续传

​需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。第一步:前端修改由于项目使用的是BJUI前端框架,并... 查看详情

html5大文件断点续传完整思路整理

需求:支持大文件批量上传(20G)和下载,同时需要保证上传期间用户电脑不出现卡死等体验;内网百兆网络上传速度为12MB/S服务器内存占用低支持文件夹上传,文件夹中的文件数量达到1万个以上,且包含层级结构。支持PC端全... 查看详情

如何让chrome断点续传

chrome也有自己的下载系统,而且速度还不错,只是好像不支持断点续传。你可以在迅雷的链接上面点右键,复制链接地址,然后到迅雷里面去新建一个任务,把地址复制到目标地址上面就可以了参考技术A看了GOOGLE还在研究这个... 查看详情

断点续传

一、重点!重点!重点!1、设置数据任务task(从路径中获取文件的长度,再判断是否下载过,来请求url,设置Range,将Range添加到请求头中)。2、写入文件(存储下载文件,通过路径设置输出流,并打开,将之前文件取出来,重新设置... 查看详情

ftpftp文件上传下载-支持断点续传

Jar包:apache的commons-net包;支持断点续传支持进度监控(有时出不来,搞不清原因)相关知识点编码格式:UTF-8等;文件类型:包括[BINARY_FILE_TYPE(常用)]和[ASCII_FILE_TYPE]两种;数据连接模式:一般使用LocalPassiveMode模式,因为大部分客户端... 查看详情

为啥chrome还不支持断点续传

参考技术Achrome内建下载是支持断点续传的只是有些下载服务器不支持而已 查看详情

afnetworking实现程序重新启动时的断点续传(代码片段)

...#xff0c;但是在进行了一番研究之后,发现AFNetworking虽然支持下载文件的暂停和继续,但是程序重新启动后再次下载无法进行续传。网上有说可以通过AFDownloadRequestOperation这个AFNetworking的扩展库来实现重新启动后的续传,... 查看详情

掀开断点续传那一层面纱(下载篇)

...DNS查找、TCP三次握手、http请求发送、TCP协议数据传输、暂停后的状态、继续下载、TCP三次握手、http请求发送、数据传输、。。。、下载成功发送http响应信息、TCP四次握手断开连接。2、原理知识  2.1、问答问答  问:什么... 查看详情

大文件上传服务器支持超大文件http断点续传实践总结(代码片段)

...s/80490621最近由于笔者所在的研发集团产品需要,需要支持高性能的大文件http上传,并且要求支持http断点续传。这里在简要归纳一下,方便记忆:服务器端由C语言实现,而不是用java、PHP这种解释型语言来实现... 查看详情

超大文件上传和断点续传的组件

...经下载的地方开始继续下载。在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了。一般断点下载时才用到Range和Content-Range实体头。HTTP协议本身不支持断点上传,需要自己实现。 二、Range  用于请求头中,指定... 查看详情

rsync支持断点续传文件

参考:https://my.oschina.net/ccLlinux/blog/1859116基本命令:rsync[OPTION]…SRC(需要备份的原文件)DEST(Push的位置)example:download:rsync-avuser@hostip:/scratch/test/test_sh.sh/Users/username/test/test.sh上传:rsync-av/Users/u 查看详情

支持断点续传的大文件传输协议

...要手段,在数据通信领域一直发挥着举足轻重的作用,不支持断点续传,是Internet上最早也是最广泛使用的应用之一.从1971年A.K.Bhushan提出第一个FTP协议版本(RFC114)到现在,人们对FTP的应用已经历了40余年的时间,同时,许多基... 查看详情

文件下载之断点续传(客户端与服务端的实现)

【转】文件下载之断点续传(客户端与服务端的实现)【转】文件下载之断点续传(客户端与服务端的实现)前面讲了文件的上传,今天来聊聊文件的下载。老规矩,还是从最简单粗暴的开始。那么多简单算简单?多粗暴算粗暴... 查看详情

java使用webuploader做大文件的分块和断点续传

​需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制。第一步:前端修改由于项目使用的是BJUI前端框架,并... 查看详情

b/s之大文件分段上传断点续传

...这些基于脚本语言实现的上传功能模块性能很弱,一是不支持2GB以上的内容上传;二是无法支持断点续传;三是效率极低,单台服务器最多支持几十个并发上传连接。当前我们要搭建一个运营级的视频服务平台,在尝试了各种产... 查看详情