实际项目中有一个需求:将远程服务器上的文件转存到本地服务器,开发中遇到很多问题,总结出一个可行的代码实例。
使用ftpClient进行文件转存,遇到很多问题:
1.retrieveFileStream获取到文件流经常是空的
2.使用completePendingCommand()经常使程序死掉
为了解决上述问题,贴出我的代码,以供参考:
FTPClient初始化:
package com.aaa.ftpservice.entity;
import lombok.Data;
@Data
public class FtpInfoEntity {
private Long id;
private String code;
private Short isSsl;
private Boolean isTls;
private String username;
private String password;
private String host;
private Integer port;
private Object pwdEnc;
}
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.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;
//method
public FTPClient getClient(FtpInfoEntity ftpInfoEntity)
{
FTPClient ftpClient = null;
try {
if (ftpInfoEntity.getIsTls()) {
ftpClient = new FTPSClient(true);
}
else {
ftpClient = new FTPClient();
}
ftpClient.setDataTimeout(600000); //设置传输超时时间为10分钟
ftpClient.setConnectTimeout(600000); //连接超时为10分钟
log.info("connect start, ftpInfoEntity is {}", ftpInfoEntity.toString());
ftpClient.connect(ftpInfoEntity.getHost(), ftpInfoEntity.getPort());
ftpClient.login(ftpInfoEntity.getUsername(), ftpInfoEntity.getPassword());
if (ftpClient instanceof FTPSClient) {
((FTPSClient) ftpClient).execPROT("P");
((FTPSClient) ftpClient).execPBSZ(0);
}
ftpClient.enterLocalPassiveMode();//开启被动模式,每次数据连接之前ftp client告诉ftp server开通 一个端口来传输数据。
ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
ftpClient.configure(conf);
int reply = ftpClient.getReplyCode();
log.info("connect reply is {}", reply);
if (!FTPReply.isPositiveCompletion(reply)) {
ftpClient.disconnect();
log.error("FTP连接错误...");
return null;
}
} catch (Exception e) {
log.error("---connect error {}", e.getMessage());
}
return ftpClient;
}
下载文件
//removeFile: d56a4ed296d311ecb6160242ac14001e/2022/08/18/b4eff63f-b0a2-4b91-a757-de0c9931e018/CCD/b05f1a66-9c6e-4a63-8a39-191c7b01c8f2.jpg
public byte[] download(FTPClient ftpClient,String remoteFile)
{
byte[] data = null;
InputStream is = null;
try {
int remoteDirIdx = remoteFile.lastIndexOf("/");
String remoteDir = remoteFile.substring(0, remoteDirIdx);
String remoteFileName = remoteFile.substring(remoteDirIdx + 1);
//这句话一定要加!!!!!!
//因为changeWorkingDirectory是根据当前的ftp路径来进行切换的,所以如果传入的removeDir绝对路径,那么一定要先把当前路径切换到根路径,再切换到removeDir,否则会找到不到文件
ftpClient.changeWorkingDirectory("/");
//这句话一定要加!!!!!!
boolean b = ftpClient.changeWorkingDirectory(remoteDir);
int length = ftpClient.listNames(remoteFileName).length;
if (length == 0) {
log.info("---remoteFile is not exist");
return null;
}
is = ftpClient.retrieveFileStream(remoteFileName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int num = is.read(buffer);
while (num != -1) {
baos.write(buffer, 0, num);
num = is.read(buffer);
}
baos.flush();
data = baos.toByteArray();
return data;
} catch (Exception e) {
log.error("---FTPUtil download1 e111 , {} ", e.getMessage());
} finally {
try {
if (Objects.nonNull(is)) {
is.close();
ftpClient.completePendingCommand();
}
} catch (Exception e1) {
log.error("---FTPUtil download1 e222 , {} ", e1.getCause());
}
}
return data;
}
保存到本地:
private void saveFileToLocal(byte[] data, String oldFilePath) throws IOException
{
FileOutputStream fileOutputStream = null;
InputStream inputStream = null;
File tempFile = null;
try {
int remoteDirIdx = oldFilePath.lastIndexOf("/");
String remoteDir = oldFilePath.substring(0, remoteDirIdx);
String remoteFileName = oldFilePath.substring(remoteDirIdx + 1);
String newFilePath = "/home/aaa/"+ remoteDir;
tempFile = new File(newFilePath);
if (!tempFile.exists()) {
tempFile.mkdirs();
}
inputStream = new ByteArrayInputStream(data);
String fileName = tempFile.getPath() + File.separator + remoteFileName;
fileOutputStream = new FileOutputStream(fileName);
// 数据缓冲
byte[] buffer = new byte[1024 * 1024];
//读取到的数据长度
int len;
// 开始读取
while ((len = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
} catch (Exception e) {
log.error("----ftp saveFileToLocal error, {}", e.getMessage());
} finally {
log.info("----ftp saveFileToLocal success,{}", oldFilePath);
fileSaveDao.deleteByPrimaryKey(fileSaveId);
if (Objects.nonNull(fileOutputStream)) {
fileOutputStream.close();
}
if (Objects.nonNull(inputStream)) {
inputStream.close();
}
if (Objects.nonNull(tempFile)) {
tempFile.delete();
}
}
}
以上为我的代码实现,以供参考
----2022.11.28分割线----
在功能测试的时候,我把保存文件的文件名添加了随机数作为后缀,导致系统上保存了一些不规则的图片文件,类似于下面的这样
111.jpg1234,111.jpg1235,111.jpg1236。
为了将这些图片的文件名改成正常的xxx.jpg文件名,我使用下面的命令行来完成。
find . -name "*.jpg*" -type f ! -name "*.jpg" -exec sh -c 'f="{}"; mv -- "$f" "${f%.jpg*}.jpg"' \;
其中:
find . -name "*.jpg*" -type f ! -name "*.jpg" --- 查找出当前目录下所有 "xxx.jpgxxx"格式名称,但不是 "xxxx.jpg" 的文件
-exec sh -c 'f="{}"; mv -- "$f" "${f%.jpg*}.jpg"' \; --- 将 "xxx.jpgxxx"格式名称的文件重命名位"xxxx.jpg"