依賴
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.5</version>
</dependency>
可選擇多個路徑進行壓縮
依賴
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.5</version>
</dependency>
可選擇多個路徑進行壓縮
依賴
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.5</version>
</dependency>
可選擇多個路徑進行壓縮
package per.qiao.utils.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.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
/**
* Create by IntelliJ Idea 2018.2
*
* @author: qyp
* Date: 2019-07-19 17:10
*/
public class FtpFileZipUtls {
private final static Logger logger = LoggerFactory.getLogger(FtpFileZipUtls.class);
/**
* 加載一個非空文件夾
*/
private static void loadFile(FTPClient ftp, String dirPath, ZipOutputStream zos) throws IOException {
FTPFile[] ftpFiles = ftp.listFiles();
// length小於0用於區分非空文件夾與空文件夾和文件的區別
// 退到上層再進入
if (ftpFiles.length <= 0 && ftp.changeToParentDirectory()) {
logger.info("退回到: " + ftp.printWorkingDirectory());
dirPath = dirPath.replaceAll("\\\\", "/");
boolean b = ftp.changeWorkingDirectory(getFileName(dirPath.substring(dirPath.lastIndexOf("/") + 1)));
logger.info("進入: " + ftp.printWorkingDirectory());
// 是一個空文件夾
if (b && (ftpFiles == null || ftpFiles.length <= 0)) {
zos.putNextEntry(new ZipEntry(getFileName(dirPath) + File.separator));
return;
}
}
for (FTPFile f : ftpFiles) {
if (f.isFile()) {
InputStream in = ftp.retrieveFileStream(f.getName());
zos.putNextEntry(new ZipEntry(getFileName(dirPath) + File.separator + f.getName()));
addToZOS(in, zos);
ftp.completePendingCommand();
} else {
if (ftp.changeWorkingDirectory(f.getName())) {
loadFile(ftp, dirPath + File.separator + f.getName(), zos);
// ftp指針移動到上一層
ftp.changeToParentDirectory();
}
}
}
}
/**
* 將文件夾中的 .替換為^^
* @param sb
* @return
*/
private static void replaceFileName(StringBuilder sb, String onepath) {
sb.replace(sb.indexOf(onepath), sb.indexOf(onepath) + onepath.length(), onepath.replaceAll("\\.", "^^"));
}
/**
* 將文件夾路徑中的 ^^ 替換成 .
* @param sb
* @return
*/
private static String getFileName(String sb) {
StringBuilder result = new StringBuilder(sb);
while (result.indexOf("^^") > -1) {
result.replace(result.indexOf("^^"), result.indexOf("^^") + 2, ".");
}
return result.toString();
}
/**
* 切換目錄 返回切換的層級數
* @param sb
* @param ftp
* @return 切換的層級數
* @throws IOException
*/
private static int exchageDir(StringBuilder sb, FTPClient ftp) throws IOException {
// 計入進入的文件夾的次數,方便回退
int level = 0;
String[] pathes = sb.toString().split("/");
for (int i = 0, len = pathes.length; i < len; i++) {
String onepath = pathes[i];
if (onepath == null || "".equals(onepath.trim())) {
continue;
}
//文件排除
boolean flagDir = ftp.changeWorkingDirectory(onepath);
if (flagDir) {
level++;
if (onepath.contains(".")) {
//把文件夾中的.用^^代替
replaceFileName(sb, onepath);
}
logger.info("成功連接ftp目錄:" + ftp.printWorkingDirectory());
} else {
logger.warn("連接ftp目錄失敗:" + ftp.printWorkingDirectory());
}
}
return level;
}
/**
* 處理單個文件和空文件夾的情況 處理了返回true 未處理返回false
* 注意:這里是指數組中給的路徑直接是文件或者空文件夾的情況
* @param ftp
* @param path 需要壓縮的文件(文件夾)路徑(相對根)
* @param zos
* @return
* @throws IOException
*/
public static boolean delFile(FTPClient ftp, StringBuilder path, ZipOutputStream zos) throws IOException {
int times = exchageDir(path, ftp);
String fileRelativePaht = path.toString();
String[] split = fileRelativePaht.split("/");
// 是否處理過
boolean isDel = false;
//有父級目錄
if (split != null && split.length > 0) {
String lastSegmeng = split[split.length - 1];
//path直接是文件
if (split[split.length - 1].contains(".")) {
zos.putNextEntry(new ZipEntry(fileRelativePaht));
addToZOS(ftp.retrieveFileStream(lastSegmeng), zos);
ftp.completePendingCommand();
isDel = true;
} else if (ftp.listFiles().length <= 0) {
// 空文件夾
zos.putNextEntry(new ZipEntry(fileRelativePaht + File.separator));
isDel = true;
}
// 如果被處理過(空文件夾或者直接給的文件的路徑),退回到根路徑(以免污染下次循環的ftp指針)
if (isDel) {
for (int i = 0; i < times; i++) {
ftp.changeToParentDirectory();
}
}
}
return isDel;
}
/**
* 添加文件到壓縮流
* @param in 輸入流(一般就直接是從FTP中獲取的)
* @param zos 壓縮輸出流
* @throws IOException
*/
public static void addToZOS(InputStream in, ZipOutputStream zos) throws IOException {
byte[] buf = new byte[1024];
//BufferedOutputStream bzos = new BufferedOutputStream(zos);
try {
for (int len; (len = (in.read(buf))) != -1; ) {
zos.write(buf, 0 , len);
}
} catch (IOException e) {
System.out.println("流轉換異常");
throw e;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
System.out.println("關流異常");
e.printStackTrace();
}
}
}
}
public static FTPClient getFtpClient(String path) throws Exception {
String server = "127.0.0.1";
int port = 21;
String username = "test";
String password = "test";
// path = "/FTPStation/";
FTPClient ftpClient = connectServer(server, port, username, password, path);
return ftpClient;
}
/**
*
* @param server
* @param port
* @param username
* @param password
* @param path 連接的節點(相對根路徑的文件夾)
* @return
*/
public static FTPClient connectServer(String server, int port, String username, String password, String path) throws IOException {
path = path == null ? "" : path;
FTPClient ftp = new FTPClient();
//下面四行代碼必須要,而且不能改變編碼格式,否則不能正確下載中文文件
// 如果使用serv-u發布ftp站點,則需要勾掉“高級選項”中的“對所有已收發的路徑和文件名使用UTF-8編碼”
ftp.setControlEncoding("GBK");
FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
conf.setServerLanguageCode("zh");
ftp.configure(conf);
// 判斷ftp是否存在
ftp.connect(server, port);
ftp.setDataTimeout(2 * 60 * 1000);
if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
ftp.disconnect();
System.out.println(server + "拒絕連接");
}
//登陸ftp
boolean login = ftp.login(username, password);
if (logger.isDebugEnabled()) {
if (login) {
logger.debug("登陸FTP成功! ip: " + server);
} else {
logger.debug("登陸FTP失敗! ip: " + server);
}
}
//根據輸入的路徑,切換工作目錄。這樣ftp端的路徑就可以使用相對路徑了
exchageDir(new StringBuilder(path), ftp);
return ftp;
}
public static void main(String[] args) throws Exception {
String[] paths = {"/CAD/你好.txt", "/CAD/1.第一層"};
String savePath = "e:/temp/zz.zip";
//連接到的節點
String rootDir = "/CAD";
FTPClient ftp = getFtpClient(rootDir);
zipFileByPaths(ftp, savePath, paths);
}
/**
* 多FTP路徑壓縮
* @param ftp
* @param savePath
* @param filePaths
* @throws Exception
*/
public static void zipFileByPaths(FTPClient ftp, String savePath, String[] filePaths) throws Exception {
try (ZipOutputStream zos = ZipUtils.getOutPutStream(savePath)) {
for (String path : filePaths) {
StringBuilder sb = new StringBuilder(path);
//delFile時進入的文件夾的路徑(當前需要遍歷的文件的路徑)
String before = ftp.printWorkingDirectory();
if (delFile(ftp, sb, zos)) {
continue;
}
loadFile(ftp, sb.toString(), zos);
// 文件加載完畢后,需要將ftp的指針退到開始狀態
String after = ftp.printWorkingDirectory();
backPath(before, after, ftp);
}
zos.flush();
} catch (Exception e) {
logger.error("多路徑文件壓縮失敗");
e.printStackTrace();
throw e;
}
}
/**
* ftp 指針回退
* @param end
* @param start
* @param ftp
* @throws IOException
*/
private static void backPath(String end, String start, FTPClient ftp) throws IOException {
long startTime = System.currentTimeMillis();
do {
if (!end.equals(start) && ftp.changeToParentDirectory()) {
start = ftp.printWorkingDirectory();
}
// 防止意外出現空循環 30s等待時間
if (System.currentTimeMillis() - startTime > 30 * 1000) {
break;
}
} while (!end.equals(start));
}
}