使用commons-net包写的一个FTP上传下载类,支持断点续传,可上传整个目录

在http://zhouzaibao.iteye.com/blog/346000基础上进行了修改

 

利用org.apache.commons.net.ftp包实现一个简单的ftp客户端实用类。主要实现一下功能

1.支持上传下载。支持断点续传

2.支持对于中文目录及中文文件创建的支持。

具体请看代码,上面有详细的注释。简化版本请参见http://zhouzaibao.iteye.com/blog/342766

 

枚举类UploadStatus代码:

 

public enum DownloadStatus {
  REMOTE_FILE_NOEXIST, // 远程文件不存在
  LOCAL_BIGGER_REMOTE, // 本地文件大于远程文件
  DOWNLOAD_FROM_BREAK_SUCCESS, // 断点下载文件成功
  DOWNLOAD_FROM_BREAK_FAILED, // 断点下载文件失败
  DOWNLOAD_NEW_SUCCESS, // 全新下载文件成功
  DOWNLOAD_NEW_FAILED; // 全新下载文件失败
}

 

枚举类DownloadStatus代码:

 

public enum UploadStatus {
  CREATE_DIRECTORY_FAIL, // 远程服务器相应目录创建失败
  CREATE_DIRECTORY_SUCCESS, // 远程服务器闯将目录成功
  UPLOAD_NEW_FILE_SUCCESS, // 上传新文件成功
  UPLOAD_NEW_FILE_FAILED, // 上传新文件失败
  FILE_EXITS, // 文件已经存在
  REMOTE_BIGGER_LOCAL, // 远程文件大于本地文件
  UPLOAD_FROM_BREAK_SUCCESS, // 断点续传成功
  UPLOAD_FROM_BREAK_FAILED, // 断点续传失败
  DELETE_REMOTE_FAILD; // 删除远程文件失败
}

 

流及文件操作工具类的代码:

 


public class FileUtil {

  /**
   * 文件拷贝
   * 
   * @param insm
   * @param outsm
   * @throws IOException
   */
  public static void copyFile(File srcFile, File destFile) throws IOException {
    if (!srcFile.exists()) {
      throw new FileNotFoundException(srcFile.getName() + "不存在!");
    }
    if (!destFile.exists()) {
      destFile.createNewFile();
    }
    InputStream in = new FileInputStream(srcFile);
    OutputStream out = new FileOutputStream(destFile);
    copyStream(in, out);
  }

  /**
   * 流拷贝,通过对流的操作完成
   * 
   * @param insm
   * @param outsm
   * @throws IOException
   */
  public static void copyStream(InputStream insm, OutputStream outsm)
      throws IOException {
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
      bis = new BufferedInputStream(insm);
      bos = new BufferedOutputStream(outsm);
      byte[] b = new byte[8192];
      int readBytes = -1;
      while ((readBytes = bis.read(b)) != -1) {
        bos.write(b, 0, readBytes);
      }
    } catch (IOException e) {
      e.printStackTrace();
      throw e;
    } finally {
      if (bis != null)
        bis.close();
      if (bos != null)
        bos.close();
    }
  }
}

 

 核心类FTPOperationProccessor代码:

 

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import cn.com.servyou.ftpuploadtool.status.DownloadStatus;
import cn.com.servyou.ftpuploadtool.status.UploadStatus;
import cn.com.servyou.ftpuploadtool.util.FileUtil;

/**
 * <p>
 * Title: FTPOperationProcessor
 * </p>
 * 
 * <p>
 * Description: FTP操作处理类
 * </p>
 * 
 * <p>
 * Copyright: Copyright (c) 2010
 * </p>
 * 
 * @author servyou
 * 
 * @version 1.0
 */
public class FTPOperationProcessor {
  private FTPClient client = new FTPClient();
  /** 默认编码 */
  public final static String ENCODING = "GBK";

  /** FTP传输用的编码 */
  public final static String FTP_ENCODING = "ISO-8859-1";

  /** 目录前缀 */
  public final static String PREFIX = "/";

  public FTPOperationProcessor() {
    super();
  }

  /**
   * 连接FTP服务器
   * 
   * @param hostname
   *            服务器IP,或主机名
   * @param port
   *            端口号
   * @param username
   *            用户名
   * @param password
   *            密码
   * @return 连接成功返回true,失败返回false
   * @throws IOException
   */
  public boolean connect(String hostname, int port, String username,
      String password) throws IOException {
    int reply;
    client.connect(hostname);
    System.out.println("Connected to " + hostname + ".");
    System.out.println(client.getReplyString());
    reply = client.getReplyCode();
    if (!FTPReply.isPositiveCompletion(reply)) {
      client.disconnect();
      System.out.println("FTP服务器拒绝连接!");
      throw new IOException("FTP服务器拒绝连接!");
    } else {
      if (client.login(username, password)) {
        client.setListHiddenFiles(true);
        return true;
      }
    }
    return false;
  }

  /**
   * 用户登出,并关闭连接
   * 
   * @throws IOException
   */
  public void disConnect() throws IOException {
    if (client.isConnected()) {
      client.logout();
      client.disconnect();
    }
  }

  /**
   * 遍历服务器的某一目录
   * 
   * @param pathname
   *            FTP服务器的路径,如/dir1/dir2/
   * @throws IOException
   */
  public void traverseDirectory(String pathname) throws IOException {
    client.changeWorkingDirectory(pathname);
    FTPFile[] fileList = client.listFiles(pathname);
    traverse(fileList);
  }

  /**
   * 遍历FTP服务器的目录
   * 
   * @param ftpClient
   *            FTPClient
   * @param fileList
   *            文件列表
   * @throws IOException
   */
  private void traverse(FTPFile[] fileList) throws IOException {
    String tempDir = null;
    for (FTPFile file : fileList) {
      if (file.getName().equals(".") || file.getName().equals("..")) {
        continue;
      }
      if (file.isDirectory()) {
        System.out.println("***************** Directory: " + file.getName()
            + "  Start **************");
        tempDir = client.printWorkingDirectory();
        if (tempDir.matches("^((/\\w+))+$"))
          tempDir += "/" + file.getName();
        else
          tempDir += file.getName();
        client.changeWorkingDirectory(new String(tempDir.getBytes(ENCODING),
            FTP_ENCODING));
        traverse(client.listFiles(tempDir));
        // 不是目录,是文件的情况
        System.out.println("***************** Directory:" + file.getName()
            + "   End **************\n");
      } else {
        System.out.println("FileName:" + file.getName() + " FileSize:"
            + file.getSize() / (1024) + "KB" + " CreateTime:"
            + file.getTimestamp().getTime());
      }
    }
    // 遍历完当前目录,就要将工作目录改为当前目录的父目录
    client.changeToParentDirectory();
  }

  /**
   * 下载单个文件
   * 
   * @param remote
   *            远端文件
   * @param local
   *            本地文件
   * @throws IOException
   */
  public DownloadStatus download(String remote, File localFile)
      throws IOException {
    client.enterLocalPassiveMode();
    client.setFileType(FTPClient.BINARY_FILE_TYPE);
    DownloadStatus result = null;
    // 检查远程文件是否存在
    FTPFile[] files = client.listFiles(new String(remote.getBytes(ENCODING),
        FTP_ENCODING));
    if (files.length != 1) {
      System.out.println("远程文件不存在");
      return DownloadStatus.REMOTE_FILE_NOEXIST;
    }
    long lRemoteSize = files[0].getSize();
    // 本地存在文件,进行断点下载
    if (localFile.exists()) {
      long localSize = localFile.length();
      // 判断本地文件大小是否大于远程文件大小
      if (localSize >= lRemoteSize) {
        System.out.println("本地文件大于远程文件,下载中止");
        return DownloadStatus.LOCAL_BIGGER_REMOTE;
      }
      // 进行断点续传,并记录状态
      FileOutputStream out = new FileOutputStream(localFile, true);
      client.setRestartOffset(localSize);
      InputStream in = client.retrieveFileStream(new String(remote
          .getBytes(ENCODING), FTP_ENCODING));
      FileUtil.copyStream(in, out);
      boolean isDo = client.completePendingCommand();
      if (isDo) {
        result = DownloadStatus.DOWNLOAD_FROM_BREAK_SUCCESS;
      } else {
        result = DownloadStatus.DOWNLOAD_FROM_BREAK_FAILED;
      }
    } else {
      localFile.createNewFile();
      FileOutputStream out = new FileOutputStream(localFile);
      InputStream in = client.retrieveFileStream(new String(remote
          .getBytes(ENCODING), FTP_ENCODING));
      FileUtil.copyStream(in, out);
    }
    return result;
  }

  /**
   * 上传文件到FTP服务器,支持断点续传
   * 
   * @param local
   *            本地文件名称,绝对路径
   * @param remote
   *            远程文件路径,使用/home/directory1/subdirectory/file.ext
   *            按照Linux上的路径指定方式,支持多级目录嵌套,支持递归创建不存在的目录结构
   * @return 上传结果
   * @throws IOException
   */
  public UploadStatus upload(String local, String remote) throws IOException {
    // 设置PassiveMode传输
    client.enterLocalPassiveMode();
    // 设置以二进制流的方式传输
    client.setFileType(FTPClient.BINARY_FILE_TYPE);
    client.setControlEncoding(ENCODING);
    UploadStatus result;
    // 对远程目录的处理
    String remoteFileName = remote;
    if (remote.contains("/")) {
      remoteFileName = remote.substring(remote.lastIndexOf("/") + 1);
      // 创建服务器远程目录结构,创建失败直接返回
      // 以下两句存在问题??
      // if (createDirectory(remote) == UploadStatus.CREATE_DIRECTORY_FAIL) {
      // return UploadStatus.CREATE_DIRECTORY_FAIL;
      // }
    }
    // 检查远程是否存在文件
    FTPFile[] files = client.listFiles(new String(remoteFileName
        .getBytes(ENCODING), FTP_ENCODING));
    if (files != null && files.length == 1) {
      long remoteSize = files[0].getSize();
      File f = new File(local);
      long localSize = f.length();
      if (remoteSize == localSize) {
        return UploadStatus.FILE_EXITS;
      } else if (remoteSize > localSize) {
        return UploadStatus.REMOTE_BIGGER_LOCAL;
      }
      // 尝试移动文件内读取指针,实现断点续传
      result = uploadFile(remoteFileName, f, remoteSize);
      // 如果断点续传没有成功,则删除服务器上文件,重新上传
      if (result == UploadStatus.UPLOAD_FROM_BREAK_FAILED) {
        if (!client.deleteFile(remoteFileName)) {
          return UploadStatus.DELETE_REMOTE_FAILD;
        }
        result = uploadFile(remoteFileName, f, 0);
      }
    } else {
      result = uploadFile(remoteFileName, new File(local), 0);
    }
    return result;
  }

  /**
   * 上传单个文件,断点续传功能
   * 
   * @param remote
   *            远程文件
   * @param localFile
   *            本地文件
   * @throws IOException
   */
  public UploadStatus uploadFile(String remote, File localFile, long remoteSize)
      throws IOException {
    UploadStatus status = null;
    long localreadbytes = 0l;
    RandomAccessFile raf = new RandomAccessFile(localFile, "r");
    OutputStream out = client.appendFileStream(new String(remote
        .getBytes(ENCODING), FTP_ENCODING));
    // 断点续传,根据FTP服务器上文件与本地机器上文件的大小比较来判断
    if (remoteSize > 0) {
      client.setRestartOffset(remoteSize);
      raf.seek(remoteSize);
      localreadbytes = remoteSize;
    }
    byte[] bytes = new byte[8096];
    int c;
    while ((c = raf.read(bytes)) != -1) {
      out.write(bytes, 0, c);
      localreadbytes += c;
    }
    if (out != null)
      out.flush();
    if (raf != null)
      raf.close();
    if (out != null)
      out.close();
    boolean result = client.completePendingCommand();
    if (remoteSize > 0) {
      status = result ? UploadStatus.UPLOAD_FROM_BREAK_SUCCESS
          : UploadStatus.UPLOAD_FROM_BREAK_FAILED;
    } else {
      status = result ? UploadStatus.UPLOAD_NEW_FILE_SUCCESS
          : UploadStatus.UPLOAD_NEW_FILE_FAILED;
    }
    return status;
  }

  /**
   * 创建目录,(远程目录格式必须是/aaa/bbb/ccc/ddd/的形式)
   * 
   * @param remote
   *            远程目录路径
   * @throws IOException
   */
  public UploadStatus createDirectory(String remote) throws IOException {
    int start = 0, end = 0;
    start = remote.startsWith("/") ? 1 : 0;
    end = remote.indexOf("/", start);
    for (; start < end;) {
      String subDirectory = remote.substring(start, end);
      if (!client.changeWorkingDirectory(new String(subDirectory
          .getBytes(ENCODING), FTP_ENCODING))) {
        // 目录不存在则在服务器端创建目录
        if (!client.makeDirectory(new String(subDirectory.getBytes(ENCODING),
            FTP_ENCODING))) {
          return UploadStatus.CREATE_DIRECTORY_FAIL;
        } else {
          client.changeWorkingDirectory(new String(subDirectory
              .getBytes(ENCODING), FTP_ENCODING));
        }
      }
      start = end + 1;
      end = remote.indexOf("/", start);
    }
    return UploadStatus.CREATE_DIRECTORY_SUCCESS;
  }

  /**
   * 递归遍历本地机器的要上传的目录,遍历的同时,在FTP服务器上创建目录
   * (如果在FTP服务器上目录不存在的话),上传文件
   * 
   * @param directory
   *            本地目录
   * @throws IOException
   */
  private void traverseLocalDirectory(File directory) throws IOException {
    File[] files = directory.listFiles();
    if (files == null || files.length == 0) {
      client.changeToParentDirectory();
      return;
    }
    for (File file : files) {
      if (file.isDirectory()) {
        String directoryName = file.getName();
        client.makeDirectory(new String(directoryName.getBytes(ENCODING),
            FTP_ENCODING));
        client.changeWorkingDirectory(new String(directoryName
            .getBytes(ENCODING), FTP_ENCODING));
        traverseLocalDirectory(file);
      } else {
        System.out.println("FileName : " + file.getName());
        upload(file.getAbsolutePath(), client.printWorkingDirectory() + "/"
            + file.getName());
      }
    }
    client.changeToParentDirectory();
  }

  /**
   * 上传本地机器的某一目录到FTP服务器的某一路径
   * 
   * @param remoteBasePath
   *            FTP服务器的一个路径
   * @param localRootDirectoryPath
   *            本地机器需要上传的目录路径
   * @throws IOException
   */
  public UploadStatus uploadDirectory(String remoteBasePath,
      String localDirectoryPath) throws IOException {
    if (createDirectory(remoteBasePath) == UploadStatus.CREATE_DIRECTORY_FAIL) {
      return UploadStatus.CREATE_DIRECTORY_FAIL;
      // remoteBasePath FTP服务器上基目录,创建成功的话
    } else {
      if (client.changeWorkingDirectory(new String(remoteBasePath
          .getBytes(ENCODING), FTP_ENCODING))) {
        File localDirectory = new File(localDirectoryPath);
        traverseLocalDirectory(localDirectory);
        return UploadStatus.CREATE_DIRECTORY_SUCCESS;
      } else {
        return UploadStatus.CREATE_DIRECTORY_FAIL;
      }
    }
  }
}

 

 

使用方法简介:

 

1.需要创建目录:

 

FTPOperationProcessor processor = new FTPOperationProcessor();
    try {
      boolean flag = processor.connect("192.168.30.190", 21, "anonymous", "");
      if (flag == false) {
        System.out.println("连接服务器失败");
        processor.disConnect();
      } else {
         processor.createDirectory("/中1/中文的/信息/还是中文的/目录是中文的/xx林寺xxa11/");
      }
    } catch (IOException e) {
      e.printStackTrace();
    }

 

    则可在FTP服务器的根目录生成目录

/中1/中文的/信息/还是中文的/目录是中文的/xx林寺xxa11

 

    2.上传整个目录到FTP服务器的某一目录:

 

 processor.uploadDirectory("/Jay Chang/Study/Example/", "D:\\My Documents\\Study\\Example\\");

 

    3.下载某一文件到本地:

 

 

processor.download("/backup/sys.gho", "f:\\sys.gho");

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值