All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.orion.net.specification.transfer.BaseFileDownloader Maven / Gradle / Ivy

The newest version!
package com.orion.net.specification.transfer;

import com.orion.lang.support.progress.ByteTransferRateProgress;
import com.orion.lang.utils.Valid;
import com.orion.lang.utils.io.FileLocks;
import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.io.Streams;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

/**
 * 大文件下载 基类 支持断点续传, 实时速率
 *
 * @author Jiahang Li
 * @version 1.0.0
 * @since 2021/3/14 13:30
 */
public abstract class BaseFileDownloader implements IFileDownloader {

    /**
     * 远程文件
     */
    protected final String remote;

    /**
     * 本地文件
     */
    protected final File local;

    /**
     * 文件锁
     */
    protected final FileLocks.NamedFileLock lock;

    /**
     * 进度条
     */
    protected final ByteTransferRateProgress progress;

    /**
     * 缓冲区大小
     */
    protected int bufferSize;

    /**
     * 是否强制覆盖下载 (不检测文件锁定/不检测文件大小/不走断点续传)
     */
    protected boolean forceOverride;

    /**
     * 文件大小相同则覆盖下载
     */
    protected boolean fileSizeEqualOverride;

    /**
     * 远程文件大小 缓存
     */
    protected Long remoteFileLength;

    public BaseFileDownloader(String remote, File local, String lockSuffix, int bufferSize) {
        Valid.notEmpty(remote, "download remote file is empty");
        Valid.notNull(local, "local file is null");
        this.remote = remote;
        this.local = local;
        this.bufferSize = bufferSize;
        this.lock = FileLocks.getSuffixFileLock(lockSuffix, local);
        this.progress = new ByteTransferRateProgress(0);
    }

    @Override
    public void forceOverride(boolean forceOverride) {
        this.forceOverride = forceOverride;
    }

    @Override
    public void fileSizeEqualOverride(boolean fileSizeEqualOverride) {
        this.fileSizeEqualOverride = fileSizeEqualOverride;
    }

    @Override
    public long getRemoteFileLength() throws IOException {
        if (remoteFileLength != null) {
            return remoteFileLength;
        }
        // 获取远程文件大小
        return this.remoteFileLength = this.getRemoteFileSize();
    }

    @Override
    public boolean checkRemoteFilePresentSizeEqual() throws IOException {
        long remoteLength = this.getRemoteFileLength();
        return remoteLength == -1 || remoteLength == local.length();
    }

    /**
     * 开始下载
     *
     * @throws IOException IOException
     */
    protected void startDownload() throws IOException {
        boolean error = false;
        try {
            // 获取远程文件大小
            long remoteSize = this.getRemoteFileLength();
            // 设置进度条终点
            progress.setEnd(remoteSize);
            // 强制覆盖下载
            if (forceOverride) {
                Files1.touch(local);
                this.download();
                return;
            }
            if (Files1.isFile(local)) {
                long localSize = local.length();
                if (localSize == remoteSize) {
                    // 文件大小一样 检测是否覆盖下载
                    if (fileSizeEqualOverride) {
                        // 如果设置文件大小一致覆盖 则重新下载
                        this.download();
                    } else {
                        // 认为是文件相同(大文件节约性能) 则跳过
                        lock.unLock();
                        progress.startTime(System.currentTimeMillis());
                        this.transferFinish();
                    }
                } else {
                    // 文件大小不一样 检测是否断点续传
                    if (lock.isLocked()) {
                        // 被锁定 继续下载
                        this.breakPointResume(localSize);
                    } else {
                        // 没被锁定 重新下载
                        this.download();
                    }
                }
            } else {
                // 直接下载
                Files1.touch(local);
                this.download();
            }
        } catch (Exception e) {
            error = true;
            throw e;
        } finally {
            progress.finish(error);
        }
    }

    /**
     * 直接下载
     *
     * @throws IOException IOException
     */
    protected void download() throws IOException {
        this.initDownload(false, 0);
        progress.start();
        lock.tryLock();
        OutputStream out = null;
        try {
            out = new BufferedOutputStream(Files1.openOutputStreamFastSafe(local), bufferSize);
            int read;
            byte[] bs = new byte[bufferSize];
            while ((read = this.read(bs)) != -1) {
                progress.accept(read);
                out.write(bs, 0, read);
            }
            lock.unLock();
        } finally {
            Streams.close(out);
            this.transferFinish();
        }
    }

    /**
     * 断点续传
     *
     * @param skip 跳过的长度
     * @throws IOException IOException
     */
    protected void breakPointResume(long skip) throws IOException {
        this.initDownload(true, skip);
        progress.setStart(skip);
        progress.setCurrent(skip);
        progress.start();
        OutputStream out = null;
        try {
            out = new BufferedOutputStream(Files1.openOutputStreamFastSafe(local, true), bufferSize);
            int read;
            byte[] bs = new byte[bufferSize];
            while ((read = this.read(bs)) != -1) {
                progress.accept(read);
                out.write(bs, 0, read);
            }
            lock.unLock();
        } finally {
            Streams.close(out);
            this.transferFinish();
        }
    }

    /**
     * 获取远程文件大小
     *
     * @return fileSize 文件不存在则返回-1
     * @throws IOException IOException
     */
    protected abstract long getRemoteFileSize() throws IOException;

    /**
     * 准开始下载
     *
     * @param breakPoint 是否为断点续传
     * @param skip       跳过的长度
     * @throws IOException IOException
     */
    protected abstract void initDownload(boolean breakPoint, long skip) throws IOException;

    /**
     * 读取数据
     *
     * @param bs bs
     * @return 长度
     * @throws IOException IOException
     */
    protected abstract int read(byte[] bs) throws IOException;

    /**
     * 传输完成回调
     *
     * @throws IOException IOException
     */
    protected abstract void transferFinish() throws IOException;

    @Override
    public ByteTransferRateProgress getProgress() {
        return progress;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy