【FTP】FTP文件上传下载-支持断点续传
- Jar包:apache的commons-net包;
- 支持断点续传
- 支持进度监控(有时出不来,搞不清原因)
相关知识点
- 编码格式: UTF-8等;
- 文件类型: 包括[BINARY_FILE_TYPE(常用)]和[ASCII_FILE_TYPE]两种;
- 数据连接模式:一般使用LocalPassiveMode模式,因为大部分客户端都在防火墙后面;
- 系统类型:UNIX/WINDOWS等,默认为Unix
流程
- 步骤1: 创建FTPClient对象,设置ftpClient属性:如编码格式、连接超时、文件上传下载进度监听器等;
- 步骤2: 使用ftpClient连接远程server:connect();
- 步骤3: 获取connect()的返回码getReplyCode(),判断是否连接成功:isPositiveCompletion();
- 步骤4: 登录远程server:login(),并转到相应目录,必要时要递归创建目录;
- 步骤5: 设置ftpClient属性:如缓存大小、文件类型、超时时间、数据连接模式等;
- 步骤6: ftp相关操作:如文件上传、下载等;
- 步骤7: 断开连接,释放资源:logout()/disconnect();
程序
FTP连接和登录

文件上传


文件下载


测试程序

完整程序
package com.sssppp.Communication;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import org.apache.commons.net.PrintCommandListener;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.io.CopyStreamEvent;
import org.apache.commons.net.io.CopyStreamListener;
/**
* FTP进行文件上传和下载;
* 支持断点续传;
*/
public final class FTPUtil {
private final FTPClient ftp = new FTPClient();
/**
*
* @param hostname
* 如:IP
* @param port
* @param username
* @param password
* @return
* @throws IOException
*/
public boolean connect(String hostname, int port, String username,
String password) throws IOException {
boolean debug = false;
if (debug) {
// 设置将过程中使用到的命令输出到控制台
this.ftp.addProtocolCommandListener(new PrintCommandListener(
new PrintWriter(System.out), true));
}
//设置系统类型
final FTPClientConfig config = new FTPClientConfig(
FTPClientConfig.SYST_UNIX);
this.ftp.configure(config);
try {
this.ftp.connect(hostname, port);
if (!FTPReply.isPositiveCompletion(this.ftp.getReplyCode())) {
this.ftp.disconnect();
System.err.println("FTP server refused connection.");
return false;
}
} catch (IOException e) {
if (this.ftp.isConnected()) {
try {
this.ftp.disconnect();
} catch (IOException f) {
}
}
System.err.println("Could not connect to server.");
e.printStackTrace();
return false;
}
if (!this.ftp.login(username, password)) {
this.ftp.logout();
System.err.println("Could not login to server.");
return false;
}
return true;
}
public void disconnect() throws IOException {
if (this.ftp.isConnected()) {
try {
this.ftp.logout();
this.ftp.disconnect();
} catch (IOException f) {
}
}
}
/**
*
* @param absSrcFileName
* @param destDir
* @param destFileName
* @throws IOException
*/
public void upLoadByFtp(String absSrcFileName, String destDir,
String destFileName) throws IOException {
// 创建并转到工作目录
String absDstDir = this.ftp.printWorkingDirectory() + "/" + destDir;
absDstDir = absDstDir.replaceAll("//", "/");
createDirectory(absDstDir, this.ftp);
// 设置各种属性
this.ftp.setFileType(FTP.BINARY_FILE_TYPE);
// Use passive mode as default because most of us are behind firewalls these days.
this.ftp.enterLocalPassiveMode();
this.ftp.setControlEncoding("utf-8");
this.ftp.setBufferSize(1024);
// 进度监听
File srcFile = new File(absSrcFileName);
this.ftp.setCopyStreamListener(new MyCopyStreamListener(srcFile.length()));
FTPFile[] files = this.ftp.listFiles(destFileName);
if (files.length == 1) {// 断点续传
long dstFileSize = files[0].getSize();
if (srcFile.length() <= dstFileSize) {// 文件已存在
return;
}
boolean b = uploadFile(destFileName, srcFile, this.ftp, dstFileSize);
if (!b) {// 如果断点续传没有成功,则删除服务器上文件,重新上传
if (this.ftp.deleteFile(destFileName)) {
uploadFile(destFileName, srcFile, this.ftp, 0);
}else {
System.err.println("Delete file fail.");
}
}
} else {
uploadFile(destFileName, srcFile, this.ftp, 0);
}
}
/**
*
* @param remoteFileName
* @param localFileName
* @throws IOException
*/
public void downLoadByFtp(String remoteFileName, String localFileName)
throws IOException {
InputStream input = null;
FileOutputStream fos = null;
// 设置各种属性
this.ftp.setBufferSize(1024);
this.ftp.setDataTimeout(1000 * 10);
this.ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
this.ftp.enterLocalPassiveMode();
// 判断远程文件是否存在
FTPFile[] files = this.ftp.listFiles(remoteFileName);
if (files.length != 1) {
System.err.println("Remote file not exist.");
return;
}
//进度监听
long remoteSize = files[0].getSize();
this.ftp.setCopyStreamListener(new MyCopyStreamListener(remoteSize));
File file = new File(localFileName);
if (file.exists()) {
long localSize = file.length();
if (localSize >= remoteSize) {
return;
}
System.out.println("@@@Break point download.@@@");
fos = new FileOutputStream(file, true);// append模式
this.ftp.setRestartOffset(localSize);
} else {
fos = new FileOutputStream(file); // override模式
}
input = this.ftp.retrieveFileStream(remoteFileName);
byte[] b = new byte[8192];
int n = 0;
while (-1 != (n = input.read(b))) {
if (Thread.currentThread().isInterrupted()) {
break;
}
fos.write(b, 0, n);
}
if (input != null) {
input.close();
}
if (fos != null) {
fos.flush();
fos.close();
}
if (!this.ftp.completePendingCommand()) {
System.err.println("Download file fail.");
this.ftp.logout();
this.ftp.disconnect();
}
}
/**
*
* @param destFileName
* @param srcFile
* @param ftpClient
* @param dstFileSize 文件写入的起始位置; >0:表示断点续传,<=0:表示上传新文件
* @return
* @throws IOException
*/
private boolean uploadFile(String destFileName, File srcFile,
FTPClient ftpClient, long dstFileSize) throws IOException {
RandomAccessFile input = null;
OutputStream fout = null;
input = new RandomAccessFile(srcFile, "r"); // 只读模式
if (dstFileSize > 0) {// 断点续传
fout = ftpClient.appendFileStream(destFileName);
input.seek(dstFileSize);
ftpClient.setRestartOffset(dstFileSize);
} else {
fout = ftpClient.storeFileStream(destFileName);
}
byte[] b = new byte[8192]; // 缓存大小
int n = 0;
while (-1 != (n = input.read(b))) {
if (Thread.currentThread().isInterrupted()) {
break;
}
fout.write(b, 0, n);
}
if (input != null) {
input.close();
}
if (fout != null) {
fout.flush();
fout.close();
}
if (!ftpClient.completePendingCommand()) {
System.err.println("Upload file fail.");
ftpClient.logout();
ftpClient.disconnect();
return false;
}
return true;
}
/**
* 在FTP服务器上创建并转到工作目录
*
* @param relativePath
* 相对工作路径,不包含文件名:如 dd/11/22/33
* @param ftpClient
* 录创建是否成功
* @return
* @throws IOException
*/
private boolean createDirectory(String relativePath, FTPClient ftpClient)
throws IOException {
if (!relativePath.startsWith("/")) {
relativePath = "/" + relativePath;
}
String dir = (ftpClient.printWorkingDirectory().equals("/") ? ""
: ftpClient.printWorkingDirectory()) + relativePath;
if (!ftpClient.changeWorkingDirectory(dir)) {
//目录不存在,则创建各级目录
for (String subDir : relativePath.split("/")) {
if (!subDir.equals("")) {
String newDir = ftpClient.printWorkingDirectory() + "/"
+ subDir;
ftpClient.mkd(newDir);
if (!ftpClient.changeWorkingDirectory(newDir)) {
return false;
}
}
}
}
return true;
}
/**
* 进度监听器
*/
private class MyCopyStreamListener implements CopyStreamListener {
private long totalSize = 0;
private long percent = -1; // 进度
/**
* 文件的总大小
* @param totalSize
*/
public MyCopyStreamListener(long totalSize) {
super();
this.totalSize = totalSize;
}
@Override
public void bytesTransferred(CopyStreamEvent event) {
bytesTransferred(event.getTotalBytesTransferred(),
event.getBytesTransferred(), event.getStreamSize());
}
//totalBytesTransferred:当前总共已传输字节数;
//bytesTransferred:最近一次传输字节数
@Override
public void bytesTransferred(long totalBytesTransferred,
int bytesTransferred, long streamSize) {
if (percent >= totalBytesTransferred * 100 / totalSize) {
return;
}
percent = totalBytesTransferred * 100 / totalSize;
System.out.println("Completed " + totalBytesTransferred + "("
+ percent + "%) out of " + totalSize + ".");
}
}
public static void main(String[] args) throws IOException {
String hostname = "10.180.137.241";
String username = "xxx";
String password = "xxx";
int port = 21;
FTPUtil ftp = new FTPUtil();
//上传文件
String absSrcFileName = "C:\\tmp\\m2eclipse1.zip";
String destDir = "ww/11/22/33";
String destFileName = "m2eclipse1.zip";
ftp.connect(hostname, port, username, password);
ftp.upLoadByFtp(absSrcFileName, destDir, destFileName);
ftp.disconnect();
// 下载文件
String localFileName = "C:\\tmp\\m2eclipse-download3333.zip";
String remoteFileName = "/ww/11/22/33/m2eclipse.zip";
ftp.connect(hostname, port, username, password);
ftp.downLoadByFtp(remoteFileName, localFileName);
ftp.disconnect();
}
}
参考链接
【FTP】FTP文件上传下载-支持断点续传的更多相关文章
- FTP文件上传并支持断点续传(一)—— win10 本地环境 ftp站点构建
由于之前项目开发是采用是采用的FTP文件上传,就一直想学习,但由于FTP服务器是公司的,为了方便就像把本地变成ftp站点,其实很简单,但也有很多坑 这里简单介绍一下自己遇到的坑 一:开通本地的ftp权 ...
- PHP 大文件上传,支持断点续传,求具体方案、源码或者文件上传插件
文件夹数据库处理逻辑 publicclass DbFolder { JSONObject root; public DbFolder() { this.root = new JSONObject(); ...
- ftp实现文件上传(下载)
例子代码 package getUrlPic; import java.io.ByteArrayInputStream; import java.io.IOException; import java ...
- 转:【专题十一】实现一个基于FTP协议的程序——文件上传下载器
引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...
- Java实现FTP批量大文件上传下载篇1
本文介绍了在Java中,如何使用Java现有的可用的库来编写FTP客户端代码,并开发成Applet控件,做成基于Web的批量.大文件的上传下载控件.文章在比较了一系列FTP客户库的基础上,就其中一个比 ...
- Python 基于Python实现Ftp文件上传,下载
基于Python实现Ftp文件上传,下载 by:授客 QQ:1033553122 测试环境: Ftp客户端:Windows平台 Ftp服务器:Linux平台 Python版本:Python 2.7 ...
- 专题十一:实现一个基于FTP协议的程序——文件上传下载器
引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...
- 【ABAP系列】SAP ABAP 实现FTP的文件上传与下载
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP 实现FTP的文 ...
- HTTP文件上传服务器-支持超大文件HTTP断点续传的实现办法
最近由于笔者所在的研发集团产品需要,需要支持高性能的大文件http上传,并且要求支持http断点续传.笔者在以前的博客如何实现支持大文件的高性能HTTP文件上传服务器已经介绍了实现大文件上传的一些基本 ...
随机推荐
- .net 下判断中英文字符串长度
System.Text.Encoding.Default.GetBytes(str).Length
- Spring的依赖注入怎么理解
先看一段代码假设你编写了两个类,一个是人(Person),一个是手机(Mobile). 人有时候需要用手机打电话,需要用到手机的dialUp方法. 传统的写法是这样: Java code public ...
- Python基于websocket实时通信的实现—GoEasy
Python websocket实时消息推送 在这里我记录一下之前如何实现服务器端与客户端实时通信: 实现步骤如下: 1. 获取GoEasy appkey. 在goeasy官网上注册一个 ...
- git 较基础命令
还需要进一步了解git的组织形式: git clone *.git 下载下来以git方式管理 如果直接下载压缩包做不到 git branch 分支相关命令 git checkout 可以换分支 git ...
- 09——绝不在构造和析构函数中调用virtual函数
在base class构造期间,virtual函数不是virtual函数. 构造函数.析构函数中不要调用virtual函数.
- redis DB操作
数据库操作 1) REDIS是全部由KEY和VALUE值构成,对数据库的增删改查操作都是基于在通过key 映射到哈希槽 然后通过哈希槽进行单向链式遍历 查找到value和具体的key. 同样 在查看 ...
- Redis的简介与安装(windows)
1.简介 Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串). list(链表).set(集合).zset(sorte ...
- IC卡复位应答ATR的数据元和它们的意义
ISO/IEC 7816-3标准中对ATR的数据串和数据元做了规定和描述.ATR的数据元和它们的意义: 数据元 说明 TS 起始字符 T0 格式字符 TA1,TB1,TC1,TD1,... 接口字符 ...
- css 居中
今天来总结一下自己知道的居中方法: 一.水平居中 1.text-align:center; 文字水平居中,也可以放在父元素中,强行让子元素居中. 2.margin: 0 auto; 使子元素在父 ...
- Java控制台中输入中文输出乱码的解决办法
Run---Run Configurations---Common---Encoding---Other---GBK Run Configurations里的Common中将编码方式改成GBK就正常了