Java操作FTP

young 533 2021-10-17

pom依赖

<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version>
</dependency>

FTP配置类

// lombok 注解
@Data
public class FtpConfigDto {
    private String ip;
    private int port;
    private String user;
    private String password;
    private String charset;
    private int connectTimeout;
    private int dataTimeout;
    private int keepAliveTimeout;
    private FTPClient ftpClient;
}

FTP文件上传数据

// lombok 注解
@Data
@AllArgsConstructor
public class FtpFileEntry {
	private String path;
	private File file;
}

FTP工具类

// lombok 注解
@Slf4j
public class FtpUtil {

	public static final String SPLITTER = "/";
	public static final int BUFFER_SIZE = 1024; public static final int INTQUANTUM_SECOND_MILLIS = 1000;

	public static void upload(FtpConfigDto config, String path, File file) {
		List<FtpFileEntry> list = new ArrayList<>();
		list.add(new FtpFileEntry(path, file));
		upload(config, list);
	}

	public static void upload(FtpConfigDto config, String path, MultipartFile multipartFile) {
		try {
			String fileName = multipartFile.getOriginalFilename();
			String prefix = fileName.substring(fileName.lastIndexOf("."));
			File file = File.createTempFile(fileName, prefix);
			multipartFile.transferTo(file);
			upload(config, path, new FileInputStream(file));
		} catch (IOException e) {
			log.info("文件上传错误 " + config.getIp(), e);
			throw new RuntimeException("文件上传错误 " + config.getIp(), e);
		}
	}

	public static void upload(FtpConfigDto config, String path, InputStream inputStream) {
		try {
			connect(config);
			String dir = getDir(path);
			String fileName = getFileName(path);
			changeWorkDir(config.getFtpClient(), dir);
			boolean isSuccess = config.getFtpClient().storeFile(convertFileName(fileName), inputStream);
			if(!isSuccess){
                throw new RuntimeExcepton("创建FTP文件失败 ");
            }
		} catch (IOException e) {
			log.info("文件上传错误 " + config.getIp(), e);
			throw new RuntimeException("文件上传错误 " + config.getIp(), e);
		} finally {
			disConnect(config.getFtpClient());
		}
	}

	public static void upload(FtpConfigDto config, List<FtpFileEntry> list) {
		validateLocalFile(list);
		try {
			connect(config);
			for (FtpFileEntry entry : list) {
				try (InputStream inputStream = new BufferedInputStream(new FileInputStream(entry.getFile()))) {
					String dir = getDir(entry.getPath());
					String fileName = getFileName(entry.getPath());
					changeWorkDir(config.getFtpClient(), dir);
					boolean isSuccess = config.getFtpClient().storeFile(convertFileName(fileName), inputStream);
					if(!isSuccess){
					    throw new RuntimeExcepton("创建FTP文件失败 ");
					}
				}
			}
		} catch (IOException e) {
			log.info("文件上传错误 " + config.getIp(), e);
			throw new RuntimeExcepton("文件上传错误 " + config.getIp(), e);
		} finally {
			disConnect(config.getFtpClient());
		}
	}

	public static InputStream readForFTP(FtpConfigDto config, String path) {
		InputStream in;
		InputStream returnIn = null;
		try {
			connect(config);
			String dir = getDir(path);
			String fileName = getFileName(path);
			changeWorkDir(config.getFtpClient(), dir);
			validateRemoteFileExist(config.getFtpClient(), dir, fileName);
			config.getFtpClient().enterLocalPassiveMode();
			in = config.getFtpClient().retrieveFileStream(convertFileName(path));
			if (in != null) {
				returnIn = cloneInputStream(in);
				in.close();
				config.getFtpClient().completePendingCommand();
			}
		} catch (IOException e) {
			log.info("FTP服务器文件读取错误 " + config.getIp(), e);
			throw new RuntimeExcepton("FTP服务器文件读取错误 " + config.getIp(), e);
		} finally {
			disConnect(config.getFtpClient());
		}
		return returnIn;
	}

	public static void download(FtpConfigDto config, String path, File file) {
		List<FtpFileEntry> list = new ArrayList<>();
		list.add(new FtpFileEntry(path, file));
		download(config, list);
	}

	public static void download(FtpConfigDto config, List<FtpFileEntry> list) {
		try {
			connect(config);
			for (FtpFileEntry entry : list) {
				try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(entry.getFile()))) {
					String dir = getDir(entry.getPath());
					String fileName = getFileName(entry.getPath());
					validateRemoteFileExist(config.getFtpClient(), dir, fileName);
					boolean isSuccess =
							config.getFtpClient().retrieveFile(convertFileName(entry.getPath()), outputStream);
					if(!isSuccess){
					    throw new RuntimeException("FTP下载文件失败");
					}
					outputStream.flush();
				}
			}
		} catch (IOException e) {
			log.info("文件下载错误 " + config.getIp(), e);
			throw new RuntimeException("文件下载错误 " + config.getIp(), e);
		} finally {
			disConnect(config.getFtpClient());
		}
	}

	public static void remove(FtpConfigDto config, String path) {
		List<String> list = new ArrayList<>();
		list.add(path);
		remove(config, list);
	}

	public static void remove(FtpConfigDto config, List<String> list) {
		try {
			connect(config);
			for (String path : list) {
				String dir = getDir(path);
				String fileName = getFileName(path);
				validateRemoteFileExist(config.getFtpClient(), dir, fileName);
				boolean isSuccess = config.getFtpClient().deleteFile(convertFileName(path));
                if (!isSuccess) {
                    throw new RuntimeException("FTP文件删除失败");
                }
			}
		} catch (IOException e) {
			log.info("文件删除错误 " + config.getIp(), e);
			throw new RuntimeException("文件删除错误 " + config.getIp(), e);
		} finally {
			disConnect(config.getFtpClient());
		}
	}

	public static void move(FtpConfigDto config, String oldFilePath, String newFilePath) {
		try {
			connect(config);
			validateRemoteFileExist(config.getFtpClient(), getDir(oldFilePath), getFileName(oldFilePath));
			boolean isSuccess = config.getFtpClient().rename(oldFilePath, newFilePath);
			if (!isSuccess) {
                throw new RuntimeException("FTP文件移动失败");
            }
		} catch (IOException e) {
			log.info("文件移动错误" + config.getIp(), e);
			throw new RuntimeException("文件移动错误" + config.getIp(), e);
		} finally {
			disConnect(config.getFtpClient());
		}
	}

	public static void rename(FtpConfigDto config, String path, String oldName, String newName) {
		try {
			connect(config);
			validateRemoteFileExist(config.getFtpClient(), path, oldName);
			changeWorkDir(config.getFtpClient(), path);
			boolean isSuccess = rename(config.getFtpClient(), oldName, newName);
            if (!isSuccess) {
                throw new RuntimeException("FTP文件重命名失败");
            }
		} catch (IOException e) {
			log.info("文件重命名错误" + config.getIp(), e);
			throw new RuntimeException("文件重命名错误" + config.getIp(), e);
		} finally {
			disConnect(config.getFtpClient());
		}
	}

	private static boolean rename(FTPClient ftpClient, String oldName, String newName) throws IOException {
		return ftpClient.rename(oldName, newName);
	}

	private static void connect(FtpConfigDto config) throws IOException {
		FTPClient ftpClient = new FTPClient();
		config.setFtpClient(ftpClient);
		ftpClient.connect(config.getIp(), config.getPort());
		if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
			boolean isLogin = ftpClient.login(config.getUser(), config.getPassword());
            if(!isLogin){
                throw new RuntimeException("FTP登陆失败");
            }
			ftpClient.setControlEncoding(config.getCharset());
			if (StandardCharsets.UTF_8.name().equals(config.getCharset())
					&& FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) {
				ftpClient.setControlEncoding(StandardCharsets.UTF_8.name());
			}
			setConfig(config);
		}
	}

	private static void setConfig(FtpConfigDto config) throws IOException {
		FTPClient ftpClient = config.getFtpClient();
		ftpClient.setBufferSize(BUFFER_SIZE);
		ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
		ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
		ftpClient.setConnectTimeout(config.getConnectTimeout() * INTQUANTUM_SECOND_MILLIS);
		ftpClient.setDataTimeout(config.getDataTimeout() * INTQUANTUM_SECOND_MILLIS);
		ftpClient.setControlKeepAliveTimeout(config.getKeepAliveTimeout() * INTQUANTUM_SECOND_MILLIS);
        // 设置被动模式
		ftpClient.enterLocalPassiveMode();
	}

	private static void disConnect(FTPClient ftpClient) {
		try {
			if (ftpClient != null && ftpClient.isConnected()) {
				ftpClient.logout();
				ftpClient.disconnect();
			}
		} catch (IOException e) {
			log.info("关闭FTP连接错误", e);
		}
	}

	private static void changeWorkDirDirect(FTPClient ftpClient, String path) throws IOException {
		boolean isSuccess = ftpClient.changeWorkingDirectory(path);
        if(!isSuccess){
            throw new RuntimeException("工作区跳转失败");
        }	
	}

	private static void changeWorkDir(FTPClient ftpClient, String dir) throws IOException {
		if (ftpClient.changeWorkingDirectory(dir)) {
			return;
		}
		StringBuilder pathTemp = new StringBuilder();
		for (String token : StrUtil.split(dir, SPLITTER)) {
			pathTemp.append(SPLITTER).append(token);
			if (!ftpClient.changeWorkingDirectory(pathTemp.toString())) {
				log.debug("目录名称为:" + token);
				boolean isSuccess = ftpClient.makeDirectory(token);
                if (!isSuccess){
                    throw new RuntimeException("创建FTP目录失败");
                }
				changeWorkDirDirect(ftpClient, pathTemp.toString());
			}
		}
	}

	private static void validateLocalFile(List<FtpFileEntry> list) {
        if(CollectionUtils.isEmpty(list)){
            throw new RuntimeException("FTP本地文件列表为空");
        }
		for (FtpFileEntry ftpUploadEntry : list) {
			FileValidate.validateExist(ftpUploadEntry.getFile());
		}
	}

	private static void validateRemoteFileExist(FTPClient ftpClient, String dir, String fileName) throws IOException {
		if (!isRemoteFileExist(ftpClient, dir, fileName)) {
			throw new RuntimeException("远程文件不存在");
		}
	}

	private static boolean isRemoteFileExist(FTPClient ftpClient, String dir, String fileName) throws IOException {
		FTPFile[] arr = ftpClient.listFiles(dir, new FTPFileFilter() {
			@Override
			public boolean accept(FTPFile ftpFile) {
				return ftpFile.getName().equals(fileName);
			}
		});
		return ArrayUtils.isNotEmpty(arr);
	}

	public static String getDir(String path) {
		int i = path.lastIndexOf(SPLITTER);
		return path.substring(0, i);
	}

	private static String getFileName(String path) {
		int i = path.lastIndexOf(SPLITTER);
		return path.substring(i + 1);
	}

	private static String convertFileName(String fileName) {
		return new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
	}

	private static InputStream cloneInputStream(InputStream input) throws IOException {
		try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
			InputStream inputStream;
			byte[] buffer = new byte[BUFFER_SIZE];
			int len;
			while ((len = input.read(buffer)) > -1) {
				baos.write(buffer, 0, len);
			}
			baos.flush();
			inputStream = new ByteArrayInputStream(baos.toByteArray());
			return inputStream;
		} catch (IOException e) {
			log.info("复制inputStream失败", e);
			throw e;
		}
	}
}