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. phpcms 换域名

    修改/caches/configs/system.php里面所有和域名有关的,把以前的老域名修改为新域名就可以了. 进行后台设置->站点管理   对相应的站点的域名进行修改. 更新系统缓存.点击 ...

  2. KinSlideshow参数设置说明

    [引用来自:http://blog.sina.com.cn/s/blog_4f4f4a5901014zn1.html] Jquery.KinSlideshow参数设置说明: 附:所有参数列表   in ...

  3. 文件夹IsShow字段为空

    IsShow为YesNo字段,默认值为Yes:在Library中新建一个文件的时候会给出默认值yes,但是新建一个文件夹的时候,默认为空,所以f.Item["IsShow"]为空, ...

  4. Sql产生自动增长的编号

    USE [DBName]GO/****** Object:  StoredProcedure [dbo].[sp_GetNo]    Script Date: 10/24/2013 19:26:44 ...

  5. Jquery全选与反选checkbox(代码示例)

    今天在公司要实现操作权限的功能,需要根据左边的树,选择一项,判断右边的操作权限,例如,增加,修改,删除,查看等按钮的显示与隐藏: 这个功能实现思路如下: 1.操作权限:增加.修改等按钮的ID和Text ...

  6. Access restriction:The type JPEGCodec is not accessible due to restriction on required library C:\Program Files\Java\jre6\lib\rt.jar

    解决方法: Project -> Properties -> libraries, 先remove掉JRE System Library,然后再Add Library重新加入. ===== ...

  7. ios专题 -动画

    知识点 : 1)ios 核心动画的基础是图层.图层的基础是CALayer,它是所有核心图层类的父类. 2)CATransaction是核心动画类负责协调多个动画原子显示更新.事务支持嵌套使用. 3)图 ...

  8. 原生Javascript实现控制DIV属性

    写在前面: 从事前端工作已有一年之久,因为工作的性质,不太涉及JS方面,所以自己的JS水平一直处于小白阶段,工作闲暇之余,在网上找了一些小项目,希望练练手,促进自己成长.这是第一篇,后续还会有很多记录 ...

  9. css文件加载:@import和link的区别

    刚看了一个百度试题:请简述CSS加载方式link和@import的区别? 平时一般都用link,也知道css的加载方式,但还真的没有仔细研究过其之间的差别,查了一些资料,大致总结如下: @impot使 ...

  10. LCS最长公共子序列HDU1159

    最近一直在学习算法,基本上都是在学习动态规划以及字符串.当然,两者交集最经典之一则是LCS问题. 首先LCS的问题基本上就是在字符串a,b之间找到最长的公共子序列,比如 YAOLONGBLOG 和 Y ...