FtpTransFile类
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
 
/**
 * 在这个版本中希望做的是软件的多线程下载
 * 现在也做了断点下载 ,是否断点下载的依据为目标文件是否已经存在 ,不管是否从断点出继续下载,
 * 分的线程下载的文件都是从指定的位置开始下载的
 * 
 */
public class FtpTransFile {
 
    private static String fileName; // 要上传或下载的文件的名字
    private static String path;// 临时文件夹的目录,用于存放多个线程下载的文件
    static long threadBlock = 100 * 1024 * 1024L;
 
    /**
     * 
     * @param path
     *            要上传的本地文件路径 如"C:/Users/repace/Desktop/zhangke1.txt";
     * @param server
     *            ftp服务器ip地址 192.168.242.133
     * @param userName
     *            登录ftp的用户名 test
     * @param password
     *            登录ftp用户名对应的密码 123456
     */
    public static void fileUpload(String OStype, String path, String server,
            String userName, String password) { // 要上传的文件的本地路径路径
        // 目前可完成单个文件的上传
 
        if (!(OStype.equalsIgnoreCase("windows") || OStype
                .equalsIgnoreCase("linux"))) {
            System.out.println("操作系统类型输入错误,应为windows或linux");
            return;
        }
 
        FTPClient ftpClient = new FTPClient();
        ftpClient.enterLocalPassiveMode(); // 这一句话一定要记得加上
        FileInputStream fis = null;
        try {
            ftpClient.connect(server);
            ftpClient.login(userName, password);
 
            File srcFile = new File(path);// 要上传的本地文件路径
            fis = new FileInputStream(srcFile);
            String storeName = srcFile.getName();// 要存储的文件的名字
            String remoteFilename = "/mnt/data/ftp/www/" + OStype.toLowerCase() + "/"
                    + storeName;
            ftpClient.changeWorkingDirectory("/mnt/data/ftp/www/" + OStype.toLowerCase()
                    + "/"); // 设置上传的文件在centos上的目录,文件上传不成功是要查看指定目录的权限
            ftpClient.setBufferSize(1024);
            ftpClient.setControlEncoding("UTF-8");
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);// 设置文件类型(二进制)
            FTPFile[] files = ftpClient.listFiles(remoteFilename);// 判断软件中心是否包含这个文件
            if (files.length == 1) {// 软件中心包含该文件
                long remoteSize = files[0].getSize();// 软件中心的文件大小
                long localSize = srcFile.length();// 打算要上传的文件大小
                if (remoteSize == localSize) { // 软件中心有这个文件,并且和打算要上传的文件大小一样,则说要上传的文件已存在
                    System.out.println("要上传的文件已存在");
                    ftpClient.disconnect();
                    return;
                } else if (remoteSize > localSize) {// 软件中心的文件比要上传的大,可能新上传的文件被修改了,然后再次上传的
                    System.out.println("软件中心的软件比即将上传的要大,无须上传或重新命名要上传的文件名");
                    ftpClient.disconnect();
                    return;
                }
                // 软件中心存的文件比要上传的文件小,则尝试移动文件内读取指针,实现断点续传 **************
                if (fis.skip(remoteSize) == remoteSize) {
                    ftpClient.setRestartOffset(remoteSize);
                    boolean i = ftpClient.storeFile(
                            new String(storeName.getBytes("UTF-8"),
                                    "iso-8859-1"), fis);
                    if (i) {
                        System.out.println("文件断点续传成功");
                        ftpClient.disconnect();
                        return;
                    }
                }
            } else { // 软件中心不包含要上传的文件,或者续传不成功,则上传全新的文件即可
                boolean i = ftpClient.storeFile(
                        new String(storeName.getBytes("UTF-8"), "iso-8859-1"),
                        fis);
                System.out.println("文件上传" + i);
            }
 
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("FTP客户端出错!", e);
        } finally {
            try {
                fis.close();
                ftpClient.disconnect();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                throw new RuntimeException("关闭FTP连接发生异常!", e);
            }
        }
    }
 
    /**
     * *
     * 
     * @param OStype
     *            操作系统的类型 windows或者是linux
     * @param fileName
     *            指出要下载的文件名字 加后缀的
     * @param storePath
     *            下载之后想要在本地的存储路径,在window系统中支持两种文件路径\\ 或者/
     * @param server
     *            ftp服务器IP地址
     * @param userName
     *            ftp分配的登录名 test
     * @param password
     *            与登录名对应的登录密码 123456
     * @throws FileNotFoundException
     * @throws InterruptedException
     */
    public static void fileDownload(String OStype, String fileNames,
            String storePath, String server, String userName, String password)
            throws FileNotFoundException, InterruptedException { // 参数是带后缀的文件名字和下载之后要存储的本地路径
        // 可完成单个文件的下载 ,
 
        if (!(OStype.equalsIgnoreCase("windows") || OStype
                .equalsIgnoreCase("linux"))) {
            System.out.println("操作系统类型输入错误,应为windows或linux");
            return;
        }
        fileName = fileNames;
 
        File file = new File(storePath);
        if (!file.exists()) {// 判断文件夹是否存在,如果不存在则创建文件夹
            file.mkdir();
        }
 
        FTPClient ftpClient = new FTPClient();
        ftpClient.enterLocalPassiveMode(); // 这一句话一定要记得加上
        String remoteFileName = "/mnt/data/ftp/www/" + OStype.toLowerCase() + "/"
                + fileName; // 服务器上的文件,前面是文件夹的名字,后面的是文件的名字
        String localFileName = "";// 本地要存储的文件绝对路径 文件夹加上文件名
 
        try {
            ftpClient.connect(server);
            ftpClient.login(userName, password);
            ftpClient.setBufferSize(1024);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); // 设置文件类型(二进制)
            FTPFile[] files = ftpClient.listFiles(remoteFileName);
            if (files.length == 0) { // 判断软件中心是否有要下载的软件
                System.out.println("软件中心没有找到要下载的软件");
                ftpClient.disconnect();
                return;
            } else { //软件中心包含请求下载的文件
 
                long localSize = 0L; // 记录本地文件的大小
                if (storePath.endsWith("\\") || storePath.endsWith("/"))// 存储路径直接是某个盘下的根目录或者用户加上了最后的斜线
                {
                    localFileName = storePath + fileName;
                    path = storePath
                            + fileName.substring(0, fileName.indexOf("."))
                            + "Temp/";
                } else {
                    localFileName = storePath + "/" + fileName;
                    path = storePath + "/"
                            + fileName.substring(0, fileName.indexOf("."))
                            + "Temp/";
                }
 
                File localFile = new File(localFileName);
                long remoteSize = files[0].getSize();// 软件中心的文件大小
                if (localFile.exists()) {// 指定下载的文件在本地文件夹内已经存在
                    localSize = localFile.length();// 已存在的文件大小
                    if (remoteSize == localSize) {
                        System.out.println("文件已下载过,无需再下载");
                        return;
                    } else if (remoteSize > localSize) { // 之前下载未完成,实现断点下载
                        System.out.println("断点下载。。。");
                    }
                    if (remoteSize < localSize) {// 如果本地的文件比软件中心的文件大,则说明本地的文件可能有错,删除,然后从头开始下载
                        localFile.delete();
                        System.out.println("软件从新开始下载");
                        localSize = 0L;
                    }
                } else {// 指定下载的文件在本地文件夹内不存在,从头下载文件
                    localSize = 0L;
                    System.out.println("软件从头下载");
                }
 
                File tempfile = new File(path);
                if (tempfile.exists()) {// 判断文件夹是否存在,如果已经存在,则删除该文件夹及其所有的子文件,以免其包含的线程影响后面的下载过程
                    System.out.println("delete 之前的临时文件夹");
                    deleteTempFile(path);
                }
                tempfile.mkdir();// 新建存放临时文件夹的目录
 
                ExecutorService exec = Executors.newCachedThreadPool(); // 开始启动多线程下载文件
                int threadNum = (int) ((remoteSize - localSize) / threadBlock + 1);// 每100M分一个线程下载
                                                                                    // 计算线程总数
                System.out.println("分成的线程个数" + threadNum);
                CountDownLatch latch = new CountDownLatch(threadNum);
                System.out.println(fileNames + "请求还需下载的文件大小"
                        + (remoteSize - localSize));
                long[] startPos = new long[threadNum];
                ChildThread[] childThreads = new ChildThread[threadNum];// ChildThread
                                                                            // 变成ChildThread1共有4处修改
                for (int i = 0; i < threadNum; i++) {
                    startPos[i] = localSize + i * threadBlock; // 设置每个线程开始下载文件的起始位置
 
                    childThreads[i] = new ChildThread(OStype, fileName,
                            storePath, server, userName, password, startPos[i],
                            i, latch); // 创建线程 线程编号从0开始
                    exec.execute(childThreads[i]);// 开始执行线程
                }
 
                latch.await(); // 等待所有的线程都运行结束
                exec.shutdown();
                tempFileToTargetFile(localFileName, childThreads, threadNum);// 把临时得到的文件夹内的文件合并到目标文件
 
            }
 
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("FTP客户端出错!", e);
        } finally {
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException("关闭FTP连接发生异常!", e);
            }
        }
    }
 
    /**
     * @author repace 把临时文件夹内的文件都写入目标文件内 即将各个线程所下的文件进行合并
     * @param target
     *            目标文件 是之前用户发送的请求要把请求下载的文件存放的绝对路径的目录
     *            比如要下载的是test.txt文件,想存在c:\\123\\文件夹内 则目标文件target
     *            指的的就是c:\\123\\test.txt
     * @param tempFile
     *            临时文件夹的目录则是 c:\\123\\testTemp\\
     * @param threadNum
     * @return
     * @throws IOException
     */
 
    public static boolean tempFileToTargetFile(String target,
            ChildThread[] childThreads, int threadNum) throws IOException { // 完成把临时文件夹内的日志都写到目标文件中
 
        System.out.println("KAISHI HEBING");
        boolean result = true;
 
        FileInputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(target, true); // 追加内容
            for (int i = 0; i < threadNum; i++) { // 遍历所有子线程创建的临时文件,按顺序把下载内容写入目标文件中
                inputStream = new FileInputStream(
                        childThreads[i].localTempFileName);
                int len = 0;
                byte[] b = new byte[1024];
                int count = 0;
                while ((len = inputStream.read(b)) != -1) {
                    outputStream.write(b, 0, len);
                    outputStream.flush();
                    count += len;
                }
                inputStream.close();
 
            }
            outputStream.flush();
            outputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (outputStream != null) {
            outputStream.close();
        }
        File file = new File(target);
        System.out.print(target + "下载得到的文件大小是 " + file.length());
        deleteTempFile(path);// 删除临时文件夹
        return result;
    }
 
    public static void deleteTempFile(String Path) {//删除临时文件夹
 
        File file = new File(Path);
        if (file.isFile()) {// 表示该文件不是文件夹
            file.delete();
        } else {
            // 首先得到当前的路径
            String[] childFilePaths = file.list();
            for (String childFilePath : childFilePaths) {
                File childFile = new File(file.getAbsolutePath() + "/"
                        + childFilePath);
                String s = childFile.getAbsolutePath();
                deleteTempFile(s);
            }
            file.delete();
        }
    }
 
}

ChildThread类

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CountDownLatch;
 
import org.apache.commons.net.ftp.FTPClient;
 
public class ChildThread extends Thread {
 
 
    public int id;
    private long startPosition;
    CountDownLatch latch;
 
    String remoteFileName;  //要下载的文件在软件中心的文件
    String localTempFileName;   //用于存放每个线程下载的临时文件的绝对路径  (带上临时文件的名字和后缀)
    String path;//临时文件夹的目录
 
    FTPClient ftpClient = new FTPClient();
 
    public ChildThread(String OStype,String fileName, String storePath,
            String server, String userName, String password,long startPos,int id,CountDownLatch latch) {
 
        ftpClient.enterLocalPassiveMode(); // 这一句话一定要记得加上
        remoteFileName = "/mnt/data/ftp/www/"+OStype.toLowerCase() +"/"+ fileName; // 服务器上的文件,前面是文件夹的名字,后面的是文件的名字
        startPosition=startPos;
        this.latch=latch;  
        this.id=id;
        try {
            ftpClient.connect(server);
            ftpClient.login(userName, password);
            ftpClient.setBufferSize(1024);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);    // 设置文件类型(二进制)    
 
            if (storePath.endsWith("\\") || storePath.endsWith("/"))//给出的路径下新建一个临时文件夹,里面存储的是各个线程下载的文件
                {
                    localTempFileName=storePath +fileName.substring(0, fileName.indexOf("."))+"Temp/" +id+"_"+fileName;//保证临时文件夹唯一 也应保证临时文件的命名唯一
                } else{
                    localTempFileName=storePath + "/" +fileName.substring(0, fileName.indexOf("."))+"Temp/" + id+"_"+fileName;
                }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("FTP客户端出错!", e);
        } finally {
        }
    }
 
 
    public void run() {
 
        FileOutputStream outputStream = null;
            try {
                File threadTempFile=new File(localTempFileName);
                outputStream = new FileOutputStream(localTempFileName,true);
                ftpClient.setRestartOffset(startPosition+threadTempFile.length());  //设置每个线程开始的下载位置  如果之前threadTempFile.length()不等于0,则从上次那个地方继续下载  断点下载
 
                InputStream in= ftpClient.retrieveFileStream(remoteFileName);
                int len = 0;
                byte[] b = new byte[1024];
                long count=threadTempFile.length();
                while((len = in.read(b)) != -1) { 
                    count +=len;//记录文件中的长度加上这次准备写的长度
                    if (count > FtpTransFile.threadBlock) { //加上最后一次读到的已经比规定的线程块大,则只取前面一部分即可
                        int lastLen= (int) (FtpTransFile.threadBlock-threadTempFile.length());
                        outputStream.write(b, 0,lastLen);//方法write(b, off, len),b[off]是写入的第一个字节和b[off+len-1]是写的这个操作的最后一个字节。
                        outputStream.flush();
                        break;
                    }
                    outputStream.write(b, 0, len);
                    outputStream.flush();
                }                
                in.close();//关闭流
                File file=new File(localTempFileName);
                System.out.println("Thread file "+id+" "+file.length());
                outputStream.close();
                ftpClient.disconnect();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        latch.countDown();//每个线程结束的时候,则总的线程数减1        
    }
 
 
 
}
 

Java ftp断点续传的更多相关文章

  1. Java FTP客户端开源类库 edtFTPj

    edtFTPj/Free是免费的流行的Java FTP库,全球公司依靠edtFTPj /Free 为它们的Java应用程序添加FTP客户端功能. (收费的支持SFTP.FTPS的edtFTPj/PRO ...

  2. 用 Java 实现断点续传 (HTTP)

    断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已. 打个比方,浏览器请求服务器上的一个文时,所发出的请求如下: 假设服务器域名为 wwww.sjtu.edu ...

  3. 用edtftpj实现Java FTP客户端工具

    edtftpj是一个java FTP工具包,使用非常方便,感觉比Apache的好用,但Apache更灵活.edtftpj有多种版本,分别是java..net和js版本.对于Java版的有一个免费版本. ...

  4. 关于Java FTP SFTP的相关实际问题

    第一个: java ftp使用的是Apache common-net,但是FTP服务侧提供的FTP服务器只支持SFTP,结果报 java.net.ConnectException: Connectio ...

  5. Java ftp上传文件方法效率对比

    Java ftp上传文件方法效率对比 一.功能简介: txt文件采用ftp方式从windows传输到Linux系统: 二.ftp实现方法 (1)方法一:采用二进制流传输,设置缓冲区,速度快,50M的t ...

  6. java 实现断点续传

    请求头一:>>>>>>>>>>>>>>>>>>>>>>>> ...

  7. (转)【Java FTP及FTP服务器搭建】

    转至 http://blog.csdn.net/studyvcmfc/article/details/8147052 目录(?)[+] -[Java FTP及FTP服务器搭建] 一:本文采用apach ...

  8. 用Java实现断点续传的基本思路和代码

    用Java实现断点续传的基本思路和代码   URL url = new URL(http://www.oschina.net/no-exist.zip); HttpURLConnection http ...

  9. 用 Java 实现断点续传参考 (HTTP)

    断点续传的原理 其实断点续传的原理很简单,就是在 Http 的请求上和一般的下载有所不同而已.        打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:        假设服务器域名为 ...

随机推荐

  1. vs2012 aspx 没有设计视图了?

    vs2012的html设计视图没有了!重新安装一次都不行!现在已经通过简单办法来解决了 其实当你打开 HTML设计器 设置时, “启用 HTML设计器"  这里是打勾的!这时千万不要放弃.先 ...

  2. Android源码编译过程之九鼎开发板

    build_kernel() { # 进入源码顶层目录 cd ${BS_DIR_KERNEL} || # 编译配置文件 make ${BS_CONFIG_KERNEL} ARCH=arm CROSS_ ...

  3. JSON 学习总结 <一>:什么是JSON

    JSON的相关资料和博客很多,JSON无处不用,最近项目中一直要用到JSON,今天没有加班,就写下,算是对自己的总结,对JSON又一次深入的认识. 废话不多了,直接进入今天的主题: 如题:今天就介绍下 ...

  4. ASP.NET MVC 第六回 过滤器Filter

    在Asp.netMvc中当你有以下及类似以下需求时你可以使用Filter功能 判断登录与否或用户权限 决策输出缓存 防盗链 防蜘蛛 本地化与国际化设置 实现动态Action Filter是一种声明式编 ...

  5. Javascript基础学习(3)_对象和数组

    一.对象是一种无序的属性集合,每个属性都有自己的名字和值. 1.创建对象 花括号内逗号分隔 var person = { "Name" : "LiCheng", ...

  6. 查看oracle 启动了多久

    想看一下系统正常运行了多少天?开机多长时间没有重启了? windows系统 C:\>systeminfo |find “系统启动时间”系统启动时间:     265 天 4 小时 26 分 32 ...

  7. 动效解析工厂:Mask 动画

    转载自:http://www.cocoachina.com/ios/20160214/15250.html 前言:很多动效都是多种动画的组合,有时候你可能只是需要其中某个动画,但面对庞杂的代码库或是教 ...

  8. IOS-UI- UIScrollView 滚动视图(1)

    滚动视图多个页面实现的原理 滚动视图位置不变 内容的位置发生改变. 滚动视图的运用1.分页查看图片 2.查看大图片 3.当内容过多需要一个页面显示,如:注册,修改个人信息等等4.当不希望用户感觉咱们的 ...

  9. C#程序:如何创建xml文件以及xml文件的增、删、改、查

    其实今天的这篇博文 ,是对请几天发表的博文的一个总结,只是想把xml文件的增删改查结合起来,这样更容易让初学的朋友理解,废话也不多说了,开始吧! 下面是我把我在vs环境下写的代码ctrl+V然后ctr ...

  10. sae的kvdb使用注意

    之前没仔细看,原来sae的kvdb使用一定要先调用初始化函数 $kv = new SaeKV(); $kv->init();//必须使用 $kv->set('index', $data);