java 使用ftp文件服务

Published on with 37 views

java 使用ftp文件服务

本文讲述如何在java做工使用ftp文件服务, 进行文件上传,下载等操作

以及如何解决中文乱码, 文件损坏等问题

依赖

 <!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
  <groupId>commons-net</groupId>
  <artifactId>commons-net</artifactId>
  <version>3.7</version>
</dependency>

解决中文文件乱码

中出现乱码一般都是由于编码问题导致

ftp传输协议定义的编码为iso-8859-1

将文件名和文件夹名转码再通过ftpClient操作

/** 转码代码参考 */
private static String to_Iso8859_1(String str) {
  if (StringUtils.isBlank(str)) return str;
  return new String(str.getBytes(), StandardCharsets.ISO_8859_1);
}

如果项目使用了指定的编码可以在字符串转byte数组时候加上指定编码

如: str.getBytes("GBK")

解决文件损坏

测试中我发现经过传输部分文件出现异常...

异常场景: 我有两张图片(2.5m的png205.8k的jpg), 这两个文件上传到ftp服务器均正常(可以正常打开, 且打开后图片无异常)

但是我再通过再javaftp协议拉到本地的时候图片出现了不同程度的异常

  • 2.5m的png: 无法打开(打开直接报文件损坏)
  • 205.8k的jpg: 可以打开, 但是图片中有部分是错位的

直接使用别的方式拉取图片到本地两张图片均正常(说明在ftp服务器上的文件是好的)

解决方案:

上传和下载均加上如下代码(使用二进制的方式传输文件)

ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

源码参考

下载地址跳转

/*******************************************************************************
 * Copyright (c) 2020. 智汇恒星科技有限公司版权所有. All rights reserved.
 * 作者:wangq
 * 公司:深圳智汇恒星科技有限公司
 * 日期:2020-09-02 15:30
 * ---
 * 项目:user-server
 * 类名:FtpUtil
 * 路径:/Users/wangq/git/home/user-server/src/main/java/com/zhhx/utils/ftp/FtpUtil.java
 * Copyright (c) 2020. 智汇恒星科技有限公司版权所有. All rights reserved.
 *
 ******************************************************************************/

package com.zhhx.utils.ftp;

import com.zhhx.utils.common.JudgeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * @Author: WanG
 * @Date: 2020/9/2 15:30
 * @version: v1.0
 * @description: ftp文件服务工具内核
 */
@Slf4j
class FtpUtilCore {

	// 日志打印前缀
	private final static String LOG_PREFIX = "FTP文件服务-";
	// 服务器根目录
	private static String ROOT_PATH;
	// ftp服务器地址
	private static String HOSTNAME;
	// ftp服务器端口号默认为21
	private static Integer PORT;
	// ftp登录账号
	private static String USERNAME;
	// ftp登录密码
	private static String PASSWORD;
	// ftp客户端
	private static FTPClient ftpClient = null;
	private static boolean flag = false;

	/**
	 * 初始化ftp core
	 *
	 * @param root_path 服务器根目录
	 * @param hostname  ftp服务器地址
	 * @param port      ftp服务器端口号默认为21
	 * @param username  ftp登录账号
	 * @param password  ftp登录密码
	 * @return
	 * @author wangq
	 * @date 2020/9/2 18:22
	 */
	public static void initCore(String root_path, String hostname, int port, String username, String password) {
		FtpUtilCore.ROOT_PATH = root_path;
		FtpUtilCore.HOSTNAME = hostname;
		FtpUtilCore.PORT = port;
		FtpUtilCore.USERNAME = username;
		FtpUtilCore.PASSWORD = password;
		flag = true;
		log.info(LOG_PREFIX + "FtpUtilCore 初始化完成, 尝试连接...");
		flag = loginFtp();

		if (flag) {
			try {
				ftpClient.disconnect();
			} catch (IOException e) {
				e.printStackTrace();
			}
			log.info(LOG_PREFIX + "FtpUtilCore 初始化成功");
		} else {
			log.warn(LOG_PREFIX + "连接失败, 请检查FTP配置");
		}
	}

	/**
	 * 连接FTP服务器
	 * 默认连接成功
	 */
	static boolean loginFtp() {

		if (!flag || JudgeUtil.isAnyNull(ROOT_PATH, HOSTNAME, PORT, USERNAME, PASSWORD)) {
			throw new RuntimeException("FtpUtilCore未初始化-需要先调用FtpUtil.initCore()进行初始化");
		}

		boolean loginResult = false;
		ftpClient = new FTPClient();
		ftpClient.setControlEncoding("UTF-8");
		try {
			log.info(LOG_PREFIX + "连接ftp服务器:" + FtpUtilCore.HOSTNAME + ":" + FtpUtilCore.PORT);

			// 连接登录FTP服务器
			ftpClient.connect(HOSTNAME, PORT);
			// 登陆FTP
			loginResult = ftpClient.login(USERNAME, PASSWORD);
			// 使用二进制传输
			ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
			ftpClient.enterLocalPassiveMode();
			if (!loginResult) {
				log.info(LOG_PREFIX + "connect failed:" + FtpUtilCore.HOSTNAME + ":" + FtpUtilCore.PORT);
			} else {

				log.info(LOG_PREFIX + "connect successfu:" + FtpUtilCore.HOSTNAME + ":" + FtpUtilCore.PORT);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return loginResult;
	}


	static boolean doUpload(String pathName, String fileName, InputStream inputStream) {
		boolean flag = false;
		try {

			loginFtp();
			log.info(LOG_PREFIX + "开始上传文件({})", fileName);
			pathName = ROOT_PATH + pathName;
			mkdir(pathName);
			ftpClient.makeDirectory(pathName);
			ftpClient.changeWorkingDirectory(pathName);
			ftpClient.storeFile(fileName, inputStream);
			inputStream.close();
			flag = true;
			log.info(LOG_PREFIX + "上传文件({})到({})成功", fileName, pathName);
		} catch (Exception e) {
			log.info(LOG_PREFIX + "上传文件失败");
			e.printStackTrace();
		} finally {
			if (ftpClient.isConnected()) {
				try {
					ftpClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (null != inputStream) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return flag;
	}

	/**
	 * 改变工作路径
	 *
	 * @param directory 工作路径
	 * @return
	 */
	static boolean changeWorkingDirectory(String directory) {
		boolean flag = true;
		try {
			flag = ftpClient.changeWorkingDirectory(directory);
			if (flag) {
				log.info(LOG_PREFIX + "进入文件夹" + directory + " 成功!");
			} else {
				log.info(LOG_PREFIX + "进入文件夹" + directory + " 失败!开始创建文件夹");
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
		return flag;
	}

	/**
	 * 创建目录
	 * 如果FTP服务器已存在该目录,则不创建;否则,创建该目录
	 *
	 * @param remoteDir 远程目录
	 * @return 成功与否
	 * @throws IOException
	 */
	static boolean mkdir(String remoteDir) throws IOException {
		boolean success = true;
		String directory = remoteDir + "/";

		// 如果远程目录不存在,则递归创建远程服务器目录
		if (!"/".equalsIgnoreCase(directory) && !changeWorkingDirectory(directory)) {
			int start;
			int end;
			if (directory.startsWith("/")) {
				start = 1;
			} else {
				start = 0;
			}
			end = directory.indexOf("/", start);
			String path = "";
			StringBuilder paths = new StringBuilder();
			do {
				String subDirectory = remoteDir.substring(start, end);
				path = path + "/" + subDirectory;
				if (!existFile(path)) {
					if (makeDirectory(subDirectory)) {
						changeWorkingDirectory(subDirectory);
					} else {
						log.info(LOG_PREFIX + "创建目录[" + subDirectory + "]失败");
						changeWorkingDirectory(subDirectory);
					}
				} else {
					changeWorkingDirectory(subDirectory);
				}

				paths.append("/").append(subDirectory);
				start = end + 1;
				end = directory.indexOf("/", start);
				// 检查所有目录是否创建完毕
			} while (end > start);
		}
		return success;
	}

	/**
	 * 判断ftp服务器文件是否存在
	 *
	 * @param path
	 * @return
	 * @throws IOException
	 */
	static boolean existFile(String path) throws IOException {
		boolean flag = false;
		FTPFile[] ftpFileArr = ftpClient.listFiles(path);
		if (ftpFileArr.length > 0) {
			flag = true;
		}
		return flag;
	}

	/**
	 * 创建文件目录
	 *
	 * @param dir 待创建目录
	 * @return 成功与否
	 */
	static boolean makeDirectory(String dir) {
		boolean flag = true;
		try {
			flag = ftpClient.makeDirectory(dir);
			if (flag) {
				log.info(LOG_PREFIX + "创建文件夹" + dir + " 成功!");

			} else {
				log.info(LOG_PREFIX + "创建文件夹" + dir + " 失败!");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return flag;
	}

	/**
	 * 下载文件
	 *
	 * @param pathName  FTP服务器文件目录
	 * @param fileName  文件名称
	 * @param localpath 下载后的文件路径
	 * @return
	 */
	static boolean doDownloadFile(String pathName, String fileName, String localpath) {
		boolean flag = false;
		boolean fileExist = false;
		OutputStream os = null;
		try {
			// 连接FTP服务
			loginFtp();
			log.info(LOG_PREFIX + "开始下载文件");
			pathName = ROOT_PATH + pathName;
			// 默认FTP登录到FTP根目录下,切换目录到待下载文件目录
			ftpClient.changeWorkingDirectory(pathName);
			FTPFile[] ftpFiles = ftpClient.listFiles();
			for (FTPFile file : ftpFiles) {

				// 查找待下载文件
				if (fileName.equalsIgnoreCase(file.getName())) {
					fileExist = true;
					File localFile = new File(localpath + "/" + file.getName());
					os = new FileOutputStream(localFile);

					// 从远程服务将文件写回到本地流
					ftpClient.retrieveFile(file.getName(), os);
					os.close();
				}
			}
			ftpClient.logout();

			if (fileExist) {
				flag = true;
				log.info(LOG_PREFIX + "下载文件成功");
			} else {
				log.info(LOG_PREFIX + "下载目录未找到该文件,文件路径: " + pathName + "/" + fileName);
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (ftpClient.isConnected()) {
				try {
					ftpClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (null != os) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return flag;
	}

	/**
	 * 删除文件
	 *
	 * @param pathName FTP服务器保存目录
	 * @param fileName 要删除的文件名称
	 * @return 成功与否
	 */
	static boolean doDeleteFile(String pathName, String fileName) {
		boolean flag = false;
		try {

			loginFtp();

			log.info(LOG_PREFIX + "开始删除文件");

			// 切换FTP目录
			ftpClient.changeWorkingDirectory(pathName);
			ftpClient.dele(fileName);
			ftpClient.logout();
			flag = true;
			log.info(LOG_PREFIX + "删除文件成功");
		} catch (Exception e) {
			log.info(LOG_PREFIX + "删除文件失败");
			e.printStackTrace();
		} finally {
			if (ftpClient.isConnected()) {
				try {
					ftpClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return flag;
	}

	/**
	 * 默认ftp传输格式是 iso-8859-1 编码
	 * 群辉中如果强制UTF-8 则不需要进行转换
	 *
	 * @return
	 */
	static String toIso8859(String str) {
		if (JudgeUtil.isNull(str)) return str;
		return new String(str.getBytes(), StandardCharsets.ISO_8859_1);
	}
}


标题:java 使用ftp文件服务
作者:TWanGT
地址:http://twangt.wang/articles/2020/09/05/1599292592306.html

Responses