package me.grass.net;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.function.Function;
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 me.grass.coder.Debug;
import me.grass.extend.PathExtend;
import me.grass.extend.StringExtend;
/**
*
* @author xxj
*/
public class FtpHelper implements Closeable {
private FTPClient ftp = null;
boolean _isLogin = false;
public static FtpHelper getInstance() {
return new FtpHelper();
}
/**
*
* ftp 匿名登录
* @param ip ftp服务地址
* @param port 端口号
* @param uname 用户名
* @param pass 密码
*/
public boolean login(String ip,int port){
//如果没有设置ftp用户可将username设为anonymous,密码为任意字符串
return login(ip, port,"anonymous","");
}
/**
*
* ftp登录
* @param ip ftp服务地址
* @param port 端口号
* @param uname 用户名
* @param pass 密码
* @param workingDir ftp 根目目录
*/
public boolean login(String ip,int port, String uname, String pass) {
ftp = new FTPClient();
// boolean flag=false;
try {
// 连接
ftp.connect(ip,port);
_isLogin = ftp.login(uname, pass);
Debug.printFormat("ftp:{0}",_isLogin?"登录成功":"登录失败");
// 检测连接是否成功
int reply = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(reply)) {
System.err.println("FTP服务器拒绝连接 ");
return false;
}
return true;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
/**
* 上传后触发
*/
public Function onUploadFileAfter;
/**
*
* ftp上传文件
* @param localFileName 待上传文件
* @param ftpDirName ftp 目录名
* @param ftpFileName ftp目标文件
* @return true||false
*/
public boolean uploadFile(String localFileName
,String ftpDirName
, String ftpFileName) {
return uploadFile(localFileName, ftpDirName, ftpFileName,false);
}
/**
*
* ftp上传文件
* @param localFileName 待上传文件
* @param ftpDirName ftp 目录名
* @param ftpFileName ftp目标文件
* @param deleteLocalFile 是否删除本地文件
* @return true||false
*/
public boolean uploadFile(String localFileName
, String ftpDirName
, String ftpFileName
, boolean deleteLocalFile) {
Debug.printFormat("准备上传 [{0}] 到 ftp://{1}/{2}"
,localFileName
,ftpDirName
,ftpFileName);
// if(StringExtend.isNullOrEmpty(ftpDirName))
// ftpDirName="/";
if(StringExtend.isNullOrEmpty(ftpFileName))
throw new RuntimeException("上传文件必须填写文件名!");
File srcFile = new File(localFileName);
if(!srcFile.exists())
throw new RuntimeException("文件不存在:"+localFileName);
try (FileInputStream fis = new FileInputStream(srcFile)) {
//上传文件
boolean flag = uploadFile(fis,ftpDirName,ftpFileName);
//上传前事件
if(onUploadFileAfter!=null){
onUploadFileAfter.apply(new FtpFileInfo(localFileName,ftpDirName,ftpFileName));
}
//删除文件
if(deleteLocalFile){
srcFile.delete();
Debug.printFormat("ftp删除源文件:{0}",srcFile);
}
fis.close();
return flag;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
}
}
/**
*
* ftp上传文件 (使用inputstream)
* @param localFileName 待上传文件
* @param ftpDirName ftp 目录名
* @param ftpFileName ftp目标文件
* @return true||false
*/
public boolean uploadFile(FileInputStream uploadInputStream
,String ftpDirName
, String ftpFileName) {
Debug.printFormat("准备上传 [流] 到 ftp://{0}/{1}"
,ftpDirName
,ftpFileName);
// if(StringExtend.isNullOrEmpty(ftpDirName))
// ftpDirName="/";
if(StringExtend.isNullOrEmpty(ftpFileName))
throw new RuntimeException("上传文件必须填写文件名!");
try {
// 设置上传目录(没有则创建)
if(!createDir(ftpDirName)){
throw new RuntimeException("切入FTP目录失败:"+ftpDirName);
}
ftp.setBufferSize(1024);
//解决上传中文 txt 文件乱码
ftp.setControlEncoding("GBK");
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
conf.setServerLanguageCode("zh");
// 设置文件类型(二进制)
ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
// 上传
String fileName = new String(ftpFileName.getBytes("GBK"),"iso-8859-1");
if(ftp.storeFile(fileName, uploadInputStream)){
uploadInputStream.close();
Debug.printFormat("文件上传成功:{0}/{1}"
,ftpDirName
,ftpFileName);
return true;
}
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
}
}
/**
* 下载文件
* @param ftpDirName ftp目录名
* @param ftpFileName ftp文件名
* @param localFileFullName 本地文件名
* @return
* @author xxj
*/
public boolean downloadFile(String ftpDirName,
String ftpFileName, String localFileFullName) {
try {
if("".equals(ftpDirName))
ftpDirName="/";
String dir = new String(ftpDirName.getBytes("GBK"),"iso-8859-1");
if(!ftp.changeWorkingDirectory(dir)){
System.out.println("切换目录失败:"+ftpDirName);
return false;
}
FTPFile[] fs = ftp.listFiles();
String fileName = new String(ftpFileName.getBytes("GBK"),"iso-8859-1");
for (FTPFile ff : fs) {
if (ff.getName().equals(fileName)) {
FileOutputStream is = new FileOutputStream(new File(localFileFullName));
ftp.retrieveFile(ff.getName(), is);
is.close();
System.out.println("下载ftp文件已下载:"+localFileFullName);
return true;
}
}
System.out.println("下载ftp文件失败:"+ftpFileName+";目录:"+ftpDirName);
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
*
* 删除ftp上的文件
*
* @param ftpFileName
* @return true || false
*/
public boolean removeFile(String ftpFileName) {
boolean flag = false;
Debug.printFormat("待删除文件:{0}"
,ftpFileName);
try {
ftpFileName = new String(ftpFileName.getBytes("GBK"),"iso-8859-1");
flag = ftp.deleteFile(ftpFileName);
Debug.printFormat("删除文件:[{0}]"
,flag?"成功":"失败");
return flag;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/**
* 删除空目录
* @param dir
* @return
*/
public boolean removeDir(String dir){
if(StringExtend.startWith(dir, "/"))
dir="/"+dir;
try {
String d = new String(dir.toString().getBytes("GBK"),"iso-8859-1");
return ftp.removeDirectory(d);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 创建目录(有则切换目录,没有则创建目录)
* @param dir
* @return
*/
public boolean createDir(String dir){
if(StringExtend.isNullOrEmpty(dir))
return true;
String d;
try {
//目录编码,解决中文路径问题
d = new String(dir.toString().getBytes("GBK"),"iso-8859-1");
//尝试切入目录
if(ftp.changeWorkingDirectory(d))
return true;
dir = StringExtend.trimStart(dir, "/");
dir = StringExtend.trimEnd(dir, "/");
String[] arr = dir.split("/");
StringBuffer sbfDir=new StringBuffer();
//循环生成子目录
for(String s : arr){
sbfDir.append("/");
sbfDir.append(s);
//目录编码,解决中文路径问题
d = new String(sbfDir.toString().getBytes("GBK"),"iso-8859-1");
//尝试切入目录
if(ftp.changeWorkingDirectory(d))
continue;
if(!ftp.makeDirectory(d)){
System.out.println("[失败]ftp创建目录:"+sbfDir.toString());
return false;
}
System.out.println("[成功]创建ftp目录:"+sbfDir.toString());
}
//将目录切换至指定路径
return ftp.changeWorkingDirectory(d);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
*
* 销毁ftp连接
*
*/
private void closeFtpConnection() {
_isLogin = false;
if (ftp != null) {
if (ftp.isConnected()) {
try {
ftp.logout();
ftp.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
*
* 销毁ftp连接
*
*/
@Override
public void close() {
this.closeFtpConnection();
}
public static class FtpFileInfo{
public FtpFileInfo(String srcFile,String ftpDirName,String ftpFileName){
this.ftpDirName=ftpDirName;
this.ftpFileName=ftpFileName;
this.srcFile=srcFile;
}
String srcFile;
String ftpDirName;
String ftpFileName;
String ftpFileFullName;
public String getSrcFile() {
return srcFile;
}
public void setSrcFile(String srcFile) {
this.srcFile = srcFile;
}
public String getFtpDirName() {
return ftpDirName;
}
public void setFtpDirName(String ftpDirName) {
this.ftpDirName = ftpDirName;
}
public String getFtpFileName() {
return ftpFileName;
}
public void setFtpFileName(String ftpFileName) {
this.ftpFileName = ftpFileName;
}
/**
* 获取ftp上传文件的完整路径名
* @return
* @author xxj
*/
public String getFtpFileFullName() {
return PathExtend.Combine("/",ftpDirName,ftpFileName);
}
}
}
三、ftp 上传单元测试
package me.grass.net;
import static org.junit.Assert.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import javax.print.attribute.standard.Finishings;
import org.apache.taglibs.standard.lang.jstl.test.beans.PublicInterface2;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import me.grass.extend.DateExtend;
import me.grass.extend.StringExtend;
import me.grass.net.FtpHelper;
/**
*
* @author xxj
*/
public class FtpHelperTest {
FtpHelper ftp=null;
@Before
public void InitBinder(){
ftp = FtpHelper.getInstance();
ftp.login("localhost", 21);//匿名登录
}
@After
public void finish(){
ftp.close();
}
@Test
public void testUplodFileStream() {
try {
String localFile="D:/Temp/resource/img.jpg";
String ftpDir="temp";
String ftpFile=StringExtend.format("img-{0}.jpg"
, DateExtend.getDate("yyyyMMddHHmmss"));
FileInputStream fi = new FileInputStream(localFile);
boolean success = ftp.uploadFile(fi,ftpDir, ftpFile);
assertTrue(success);
localFile="D:/Temp/resource/文本.txt";
ftpDir="temp";
ftpFile=StringExtend.format("文本-{0}.txt"
, DateExtend.getDate("yyyyMMddHHmmss"));
fi = new FileInputStream(localFile);
success = ftp.uploadFile(fi,ftpDir, ftpFile);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
public void testOnAfter() {
String localFile="D:/Temp/resource/img.jpg";
String ftpDir="/aa/bb";
String ftpFile=StringExtend.format("img-{0}.jpg"
, DateExtend.getDate("yyyyMMddHHmmss"));
// 上传后事件
ftp.onUploadFileAfter = (ftpFileInfo) -> {
try(FileInputStream fstream = new FileInputStream(new File(ftpFileInfo.getSrcFile()))) {
ByteArrayOutputStream bstream = new ByteArrayOutputStream();
byte[] buffer = new byte[1000];
int n = 0;
while ((n = fstream.read(buffer)) != -1) {
bstream.write(buffer, 0, n);
}
System.out.println("onafter:stream lenth = "+bstream.size());
bstream.close();
fstream.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
};
//上传
boolean success = ftp.uploadFile(localFile,ftpDir, ftpFile);
assertTrue(success);
}
@Test
public void testUplodaFile() {
String localFile="D:/Temp/resource/img.jpg";
String ftpDir="/aa/bb";
String ftpFile=StringExtend.format("img-{0}.jpg"
, DateExtend.getDate("yyyyMMddHHmmss"));
boolean success = ftp.uploadFile(localFile,ftpDir, ftpFile);
assertTrue(success);
localFile="D:/Temp/resource/text.txt";
ftpDir="/aa/bb/c1";
ftpFile=StringExtend.format("text-{0}.txt"
, DateExtend.getDate("yyyyMMddHHmmss"));
success = ftp.uploadFile(localFile,ftpDir, ftpFile);
assertTrue(success);
localFile="D:/Temp/resource/文本.txt";
ftpDir="/aa/bb/c2";
ftpFile=StringExtend.format("文本-{0}.txt"
, DateExtend.getDate("yyyyMMddHHmmss"));
success = ftp.uploadFile(localFile,ftpDir, ftpFile);
assertTrue(success);
}
@Test
public void testRemove(){
boolean success = false;
String file ="/temp/文本-20170509175507.txt";
success = ftp.removeFile(file);
assertTrue(success);
}
@Test
public void crateDir(){
String dir="aa/bb";
boolean success = ftp.createDir(dir);
System.out.print(success?"成功":"失败");
System.out.println(dir);
dir="aa/bb/c1";
success = ftp.createDir(dir);
System.out.print(success?"成功":"失败");
System.out.println(dir);
dir="aa/bb/c2";
success = ftp.createDir(dir);
System.out.print(success?"成功":"失败");
System.out.println(dir);
assertTrue(success);
}
@Test
public void removeDir(){
String dir="aa/bb";
boolean success = ftp.removeDir(dir);
System.out.print(success?"成功":"失败");
System.out.println(dir);
dir="aa/bb/c1";
success = ftp.removeDir(dir);
System.out.print(success?"成功":"失败");
System.out.println(dir);
dir="aa/bb/c2";
success = ftp.removeDir(dir);
System.out.print(success?"成功":"失败");
System.out.println(dir);
assertTrue(success);
}
@Test
public void downloadFile(){
String ftpDirName="/aa/bb";
String ftpFileName="img-20170606110355.jpg";
String localFileFullName=StringExtend.format("D:/Temp/pdf/down/down-{0}.jpg"
,DateExtend.getDate("yyyyMMddHHmmss"));
boolean result = ftp.downloadFile(ftpDirName, ftpFileName, localFileFullName);
assertTrue(result);
ftpDirName="/";
ftpFileName="文本.txt";
localFileFullName=StringExtend.format("D:/Temp/pdf/down/down-{0}.txt"
,DateExtend.getDate("yyyyMMddHHmmss"));
result = ftp.downloadFile(ftpDirName, ftpFileName, localFileFullName);
assertTrue(result);
}
}
三个工具类:StringExtend,PathExtend,Debug工具类
package me.grass.extend;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.regex.Pattern;
import org.apache.taglibs.standard.lang.jstl.test.beans.PublicInterface2;
import org.springframework.beans.NullValueInNestedPathException;
public class StringExtend {
/**
* 获取换行符,区分不同系统
* /r Mac;/n Unix/Linux;/r/n Windows
* @return
* @author admin
*/
public static String getEnterMark(){
return System.getProperty("line.separator");
}
/**
* 清除首位空格
* @return
*/
public static String trim(String msg)
{
if(msg==null)
return null;
return msg.trim();
}
/**
* 字符串内容格式化输出,内部使用{0}\{1}\{2}...为参数占位符
* 参数格式:ArgumentIndex[,FormatType[,FormatStyle]]
* FormatType 取值:number,date,time,choice
* FormatType 样式:如:#.##
* 注:'{' 可输出左花括号(单写左花括号会报错,而单写右花括号将正常输出)
* @param msg 格式化模板
* @param args 不固定参数
* @return
*/
public static String format(String msg, Object... args)
{
return java.text.MessageFormat.format(msg, args);
}
/**
* 转换字符串到
* @param num
* @return
* @author xxj
*/
public static Integer getInt(String num){
if(num==null || num.trim().isEmpty())
return 0;
if(!num.matches("^(\\d|-)\\d{0,9}$"))
return 0;
try {
return Integer.parseInt(num);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return 0;
}
}
public static String getString(Object obj){
return obj == null ? null : obj.toString();
}
public static String getString(Integer num){
return getString(num,"");
}
public static String getString(Integer num,String def){
if(num == null)
return def;
return num.toString();
}
public static String getString(Date date){
return DateExtend.getString(date);
}
/**
* 字符串是否为空
* @param str
* @return
*/
public static boolean isNullOrEmpty(String str){
return str==null || str.trim().isEmpty();
}
/**
* 比较两个字符串是否相等,忽略大小写
* @param str1
* @param str2
* @return
* @author xxj
*/
public static boolean equalsIgnoreCase(String str1 ,String str2){
String tmp = str1==null?"":str1;
return tmp.equalsIgnoreCase(str2);
}
/**
* md5 加密
* @param str
* @return
* @author xxj 2017年4月24日
*/
public static String getMd5(String... str){
if(str==null || str.length==0)
return "";
StringBuffer sbr = new StringBuffer();
for(String item : str){
sbr.append(item);
}
// 生成一个MD5加密计算摘要
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
// 计算md5函数
md.update(sbr.toString().getBytes());
// digest()最后确定返回md5 hash值,返回值为8为字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
// BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
return new BigInteger(1, md.digest()).toString(16);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "";
}
}
/**
* 删除起始字符
* @param s
* @return
* @author xxj 2017年4月27日
*/
public static String trimStart(String str,String trim){
if(str==null)
return null;
return str.replaceAll("^("+trim+")+", "");
}
/**
* 删除末尾字符
* @param s
* @return
* @author xxj 2017年4月27日
*/
public static String trimEnd(String str,String trim){
if(str==null)
return null;
return str.replaceAll("("+trim+")+$", "");
}
/**
* 以字符开头
* @param s
* @return
* @author xxj 2017年4月27日
*/
public static boolean startWith(String str,String s){
return str.startsWith(s);
}
/**
* 以字符末尾
* @param s
* @return
* @author xxj 2017年4月27日
*/
public static boolean endWith(String str,String s){
return str.endsWith(s);
}
/**
* 获取 boolean 值(1=true;True=true;)
* @param str
* @return
* @author xxj 2017年5月2日
*/
public static boolean getBoolean(String str){
if(isNullOrEmpty(str))
return false;
Pattern pattern = Pattern.compile("(1)|(true)", Pattern.CASE_INSENSITIVE);
if(pattern.matcher(str).matches())
return true;
return false;
}
/**
* 隐藏银行账号后6位
* @param str
* @return
*/
public static String bankAccount(String str) {
if(isNullOrEmpty(str)){
return null;
}
if (str.length()>6) {
return str.substring(0, str.length()-6)+"xxxxxx";
}
return str;
}
}
package me.grass.extend;
import java.io.File;
/**
* 路径扩展
* @author xxj
* @version 创建时间:2017年4月27日 下午2:45:39
*/
public class PathExtend {
/**
* 合并路径
* @param args
* @return
* @author xxj 2017年4月27日
*/
public static String Combine(String ...args){
if(args==null || args.length==0)
return "";
StringBuffer sbf = new StringBuffer();
for(String s:args){
// //纯协议开头不处理,如:http://,d:/,linux首个/不处理
// if(s.matches("^[a-zA-z]+://$")){
// sbf.append(s);
// continue;
// }
//首位地址只删除尾部正反斜杠
if(sbf.length()==0){
sbf.append(s.replaceAll("/{1,}$|\\{1,}$", ""));
continue;
}
if(sbf.length()>0)
sbf.append("/");
//去除首尾正反斜杠
sbf.append(s
.replaceAll("^/{1,}|^\\{1,}", "")
.replaceAll("/{1,}$|\\{1,}$", ""));
}
return sbf.toString();
}
/**
* 获取应用程序 classpath 路径
* @return
* @author xxj 2017年4月27日
*/
public static String getClassPath(){
return PathExtend.class.getResource("/").getPath();
}
/**
* 将相对路径转为绝对路径(相对与 calsspath 的路径)
* @param relativePath
* @return
* @author xxj 2017年4月27日
*/
public static String getAbsolutePath(String relativePath){
return Combine(getClassPath(),relativePath);
}
/**
* 获取路径中的目录部分
* @param path
* @return
* @author xxj 2017年6月15日
*/
public static String getDirectory(String path){
return path.replaceAll("(/)([^/])+\\.([^/])+$", "");
}
/**
* 获取路径中的文件名部分
* @param path
* @return
* @author xxj 2017年6月15日
*/
public static String getFileName(String path){
return path.replaceAll("^.+(/)", "");
}
/**
* 创建目录(存在则不创建)
* @return
* @author xxj 2017年6月15日
*/
public static boolean createDirectory(String dirName){
File file=new File(dirName);
if(file.exists())
return true;
return file.mkdirs();
}
}
package me.grass.coder;
import me.grass.extend.StringExtend;
/**
*
* @author xxj
* @version 创建时间:2017年4月26日 上午9:52:27
*/
public class Debug {
/**
* 格式化输出,打印信息到控制台
* @param format
* @param args
* @author xxj 2017年4月26日
*/
public static void printFormat(String format,Object ...args){
if(args==null){
System.out.println(format);
}
System.out.println(java.text.MessageFormat.format(format, args));
}
/**
* 格式化输出,打印信息到控制台
* @param format
* @param args
* @author xxj 2017年4月26日
*/
public static void print(Object ...msg){
if(msg==null){
return;
}
for(Object x:msg){
System.out.print(x);
// System.out.print(' ');
}
System.out.println();
}
/**
* 格式化输出,打印信息到控制台
* @param format
* @param args
* @author xxj 2017年4月26日
*/
public static void println(Object ...msg){
if(msg==null){
return;
}
for(Object x:msg)
System.out.println(x);
}
/**
* 打印当前线程的调用堆栈
*
* @author xxj 2017年4月26日
*/
public static void printTrack(){
StackTraceElement[] st = Thread.currentThread().getStackTrace();
if(st==null){
System.out.println("无堆栈...");
return;
}
StringBuffer sbf =new StringBuffer();
sbf.append(StringExtend.format("调用堆栈[{0}]:",StringExtend.getString(new java.util.Date())));
for(StackTraceElement e:st){
sbf.append(StringExtend.format(" {0}.{1}() {2} <- {3}"
,e.getClassName()
,e.getMethodName()
,e.getLineNumber()
,StringExtend.getEnterMark()));
}
System.out.println(sbf.toString());
}
}