【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文件上传服务器已经介绍了实现大文件上传的一些基本 ...
随机推荐
- canvas转盘抽奖
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" ...
- vim 分屏显示
我用vim打开一个文件后,想同时打开另一个文件,就像windows中打开两个记事本一样,因此需要分屏显示 首先用vim打开一个文件 vim file1 输入命令[Esc] :sp file2 分屏打开 ...
- 兼容解决 IE 、火狐、谷歌浏览器中 Iframe框架的页面缓存的方法
<script type="text/javascript"> document.write('<iframe src="/ad_footer.html ...
- 深入理解css系列:css定位
一.概述 1.默认文档流定位方式 (1).HTML默认文档以流模式定位,即内容元素按照先后顺序依次上下定位: (2).HTML标签元素总体分为块状元素.内联元素.内联块状元素,可通过该标签对应的DOM ...
- 信号量与PV操作
在计算机操作系统中,PV操作是进程管理中的难点.首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下: P(S):①将信号量S的 ...
- C#常用操作类库五(电脑操作类)
/// <summary> /// Computer Information /// </summary> public class ComputerHelper { publ ...
- Microsoft Visual C++
Microsoft Visual C++ Visual C++ 1.0 1992 Visual C++ 1.5 Visual C++ 2.0 (备注) Visual C++ 4.0 Visual C+ ...
- 最全的CSS浏览器兼容问题
CSS对浏览器的兼容性有时让人很头疼,或许当你了解当中的技巧跟原理,就会觉得也不是难事,从网上收集了IE7,6与Fireofx的兼容性处理方法并整理了一下.对于web2.0的过度,请尽量用xhtml格 ...
- powerdesigner 生成mysql PDM 的COMMENT注释
1powerdesigner 生成mysql PDM 的COMMENT注释 默认的pd没有生成注释,针对mysql5.0可以如下修改.在Database-->edit Current DBMS. ...
- Hql 中 dao 层 以及daoimpl 层的代码,让mvc 模式更直观简洁
1.BaseDao接口: //使用BaseDao<T> 泛型 ,在service中注入的时候,只需要将T换为对应的bean即可 public interface BaseDao<T& ...