前言:该工具类使用的是apache的org.apache.commons.net.ftp.FTPClient。
需要导入commons-net依赖:
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
1、FTPUtil:
注:此工具类在windows和linux下面都能使用!
public class FTPUtil {
/** 本地字符编码 */
private static String LOCAL_CHARSET = "GBK";
// FTP协议里面,规定文件名编码为iso-8859-1
private static String SERVER_CHARSET = "ISO8859-1";
/**
* 文件上传
* @param ip ftp服务器的ip地址
* @param port ftp服务器的端口
* @param username 用户名
* @param password 密码
* @param uploadPath 目标路径
* @param file 被上传的文件
* @return 上传成功返回true
*/
public static boolean upload(String ip, int port, String username, String password, String uploadPath, File file) {
boolean result = false;
FTPClient ftpClient = getFTPClient(ip, port, username, password);
if (ftpClient == null) {
file.delete();
return result;
}
try {
if (uploadPath.startsWith("/")) {
uploadPath = uploadPath.substring(1);
}
String[] paths = uploadPath.split("/");
for (String path: paths) {
if (!ftpClient.changeWorkingDirectory(path)) {
ftpClient.makeDirectory(path);
}
ftpClient.changeWorkingDirectory(path);
}
String fileName = file.getName();
fileName = new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
result = ftpClient.storeFile(fileName, in);
in.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
close(ftpClient);
}
return result;
}
/**
* 单文件下载
* @param ip ftp服务器的ip地址
* @param port ftp服务器的端口
* @param username 用户名
* @param password 密码
* @param response 响应对象
* @param fileName 被下载的文件名
* @param downloadPath 被下载文件在ftp上的路径
* @return 下载成功返回true
*/
public static boolean download(String ip, int port, String username, String password, HttpServletResponse response, String fileName, String downloadPath) {
boolean result = false;
FTPClient ftpClient = getFTPClient(ip, port, username, password);
if (ftpClient == null) {
return result;
}
try {
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET));
ftpClient.changeWorkingDirectory(downloadPath);
FTPFile[] files = ftpClient.listFiles();
for (FTPFile file: files) {
if (file.getName().equals(fileName)) {
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
result = ftpClient.retrieveFile(new String(file.getName().getBytes(LOCAL_CHARSET), SERVER_CHARSET), out);
out.close();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(ftpClient);
}
return result;
}
/**
* 删除文件
* @param ip ftp服务器的ip地址
* @param port ftp服务器的端口
* @param username 用户名
* @param password 密码
* @param fileName 被删除的文件名
* @param serverPath 被删除文件在ftp上的路径
* @return 删除成功返回true
*/
public static boolean remove(String ip, int port, String username, String password, String fileName, String serverPath) {
boolean result = false;
FTPClient ftpClient = getFTPClient(ip, port, username, password);
if (ftpClient == null) {
return result;
}
try {
if (!serverPath.endsWith("/")) {
serverPath += "/";
}
ftpClient.changeWorkingDirectory(serverPath);
FTPFile[] files = ftpClient.listFiles();
for (FTPFile file: files) {
if (file.getName().equals(fileName)) {
result = ftpClient.deleteFile(serverPath + new String(file.getName().getBytes(LOCAL_CHARSET), SERVER_CHARSET));
break;
}
}
boolean flag = removeDirectory(ftpClient, serverPath);
if (!(result && flag)) {
result = false;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close(ftpClient);
}
return result;
}
/**
* 多文件打包zip下载
* @param ip ftp服务器的ip地址
* @param port ftp服务器的端口
* @param username 用户名
* @param password 密码
* @param response 响应对象
* @param fileEntryList 被下载的文件名和路径对应的列表
* @param zipFileName 压缩文件名称
* @return 下载成功返回true
*/
public static boolean zipDownload(String ip, int port, String username, String password, HttpServletResponse response, List<FileEntry> fileEntryList, String zipFileName) {
boolean result = false;
FTPClient ftpClient = getFTPClient(ip, port, username, password);
if (ftpClient == null) {
return result;
}
ZipOutputStream zip;
try {
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + new String(zipFileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET));
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
zip = new ZipOutputStream(out);
for (FileEntry fileEntry: fileEntryList) {
String fileName = fileEntry.getFileName();
String path = fileEntry.getFilePath();
ftpClient.changeWorkingDirectory(path);
FTPFile[] files = ftpClient.listFiles();
for (FTPFile file: files) {
if (file.getName().equals(fileName)) {
if (!path.endsWith("/")) {
path += "/";
}
if (path.startsWith("/")) {
path = path.substring(1);
}
zip.putNextEntry(new ZipEntry(path + fileName));
BufferedInputStream in = new BufferedInputStream(ftpClient.retrieveFileStream(new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET)));
byte[] buff = new byte[1024];
int len;
while((len = in.read(buff)) != -1) {
zip.write(buff, 0, len);
}
zip.closeEntry();
in.close();
ftpClient.completePendingCommand();
break;
}
}
}
zip.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
close(ftpClient);
}
return result;
}
/**
* 网页直接显示ftp上的图片
* @param ip ftp服务器的ip地址
* @param port ftp服务器的端口
* @param username 用户名
* @param password 密码
* @param response 响应对象
* @param fileName 图片名称
* @param serverPath ftp上的路径
*/
public static void showImage(String ip, int port, String username, String password, HttpServletResponse response, String fileName, String serverPath) {
FTPClient ftpClient = getFTPClient(ip, port, username, password);
BufferedInputStream in;
BufferedOutputStream out;
if (ftpClient == null) {
return;
}
try {
response.setContentType("image/*");
out = new BufferedOutputStream(response.getOutputStream());
ftpClient.changeWorkingDirectory(serverPath);
FTPFile[] files = ftpClient.listFiles();
for (FTPFile file: files) {
if (file.getName().equals(fileName)) {
in = new BufferedInputStream(ftpClient.retrieveFileStream(new String(file.getName().getBytes(LOCAL_CHARSET), SERVER_CHARSET)));
if (in == null) {
return;
}
byte[] buff = new byte[1024 * 10];
int len;
while((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
in.close();
out.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
close(ftpClient);
}
}
/*
获取连接
*/
private static FTPClient getFTPClient(String ip, int port, String username, String password) {
FTPClient ftp = new FTPClient();
try {
ftp.connect(ip, port);
ftp.login(username, password);
int replyCode = ftp.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
ftp.disconnect();
return null;
}
// 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK).
int command = ftp.sendCommand("OPTS UTF8", "ON");
if (FTPReply.isPositiveCompletion(command)) {
LOCAL_CHARSET = "UTF-8";
}
//设置ftp连接超时,单位毫秒,默认是0,即无限超时
ftp.setDataTimeout(120000);
ftp.setControlEncoding(LOCAL_CHARSET);
// 设置被动模式
ftp.enterLocalPassiveMode();
ftp.setFileType(FTP.BINARY_FILE_TYPE);
} catch (IOException e) {
e.printStackTrace();
}
return ftp;
}
/*
关闭连接
*/
private static boolean close(FTPClient ftpClient) {
if (ftpClient == null) {
return false;
}
try {
ftpClient.logout();
ftpClient.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
/*
递归删除文件夹
*/
private static boolean removeDirectory(FTPClient ftpClient, String serverPath) {
boolean result = true;
try {
FTPFile[] files = ftpClient.listFiles();
if (files.length == 0) {
if (serverPath.endsWith("/")) {
serverPath = serverPath.substring(0, serverPath.lastIndexOf("/"));
}
String[] paths = new String[2];
paths[0] = serverPath.substring(0, serverPath.lastIndexOf("/"));
paths[1] = serverPath.substring(serverPath.lastIndexOf("/") + 1);
if (ftpClient.changeToParentDirectory()) {
ftpClient.removeDirectory(paths[1]);
if (!paths[0].isEmpty()) {
result = removeDirectory(ftpClient, paths[0]);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
}
2、controller:
注:打包压缩包下载时使用了实体类,而不是使用Map,是因为键有可能重复的情况,用实体类可以解决这一局限!
@Controller
public class UploadController {
private static final String IP = "192.168.1.26";
private static final int PORT = 21;
private static final String USERNAME = "test";
private static final String PASSWORD = "123456";
@RequestMapping("/getUploadPage")
public String getUploadPage() {
return "ajax-prototype-upload";
}
@RequestMapping("/upload")
@ResponseBody
public String upload(MultipartFile[] upFiles) {
for (MultipartFile upFile : upFiles) {
String fileName = upFile.getOriginalFilename();
File file = new File(fileName);
String uploadPath = "/test/abc/";
try {
upFile.transferTo(file);
FTPUtil.upload(IP, PORT, USERNAME, PASSWORD, uploadPath, file);
//transferTo会生成一个临时文件,上传完成后需要调用delete删除此文件
boolean b = file.delete();
System.out.println(b);
} catch (IOException e) {
e.printStackTrace();
}
}
return "success";
}
@RequestMapping("/download")
public void download(HttpServletResponse response) {
String fileName = "1.bmp";
String downloadPath = "/test/abc/";
FTPUtil.download(IP, PORT, USERNAME, PASSWORD, response, fileName, downloadPath);
}
@RequestMapping("/zipDownload")
public void zipDownload(HttpServletResponse response) {
String fileName = "压缩包测试.zip";
List<FileEntry> fileEntryList = new ArrayList<>();
FileEntry fileEntry = new FileEntry();
fileEntry.setFileName("1.bmp");
fileEntry.setFilePath("/test/abc/");
fileEntryList.add(fileEntry);
fileEntry = new FileEntry();
fileEntry.setFileName("2.bmp");
fileEntry.setFilePath("/test/abc/");
fileEntryList.add(fileEntry);
fileEntry = new FileEntry();
fileEntry.setFileName("1.bmp");
fileEntry.setFilePath("/test/");
fileEntryList.add(fileEntry);
fileEntry = new FileEntry();
fileEntry.setFileName("1.bmp");
fileEntry.setFilePath("/");
fileEntryList.add(fileEntry);
FTPUtil.zipDownload(IP, PORT, USERNAME, PASSWORD, response, fileEntryList, fileName);
}
@RequestMapping("/remove")
@ResponseBody
public String remove(@RequestParam("file") String file) {
String serverPath = "/test/abc/";
boolean result = FTPUtil.remove(IP, PORT, USERNAME, PASSWORD, file, serverPath);
if (result) {
return "success";
} else {
return "failed";
}
}
@RequestMapping("/showImage")
public void showImage(HttpServletResponse response) {
String fileName = "1.bmp";
String serverPath = "/test/";
FTPUtil.showImage(IP, PORT, USERNAME, PASSWORD, response, fileName, serverPath);
}
}
3、FileEntry实体类:
public class FileEntry {
private String fileName;
private String filePath;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
@Override
public String toString() {
return "FileEntry{" +
"fileName='" + fileName + '\'' +
", filePath='" + filePath + '\'' +
'}';
}
}
4、html:
<html>
<head>
<title>ajax-prototype-upload</title>
<script src="js/jquery.js"></script>
</head>
<body id="body">
选择文件:<input id="file" type="file" name="upFiles" multiple/>
<input id="upload" type="button" value="上传"/><br/>
<input id="download" type="button" value="下载">
<button id="remove">删除</button>
<button id="zipDownload">多文件下载</button>
<button id="showImage">展示图片</button>
<script>
$(function() {
$("#upload").on('click', function() {
let data = new FormData();
for (let i = 0; i < $('#file')[0].files.length; i++) {
data.append('upFiles', $('#file')[0].files[i]);
}
$.ajax({
url: '/upload',
method: 'POST',
data: data,
type: 'POST',
dataType: 'TEXT',
contentType: false,
processData: false,
success: function(msg) {
alert(msg);
}
});
});
$('#download').on('click', function() {
window.location.href = "/download";
});
$('#zipDownload').on('click', function() {
window.location.href = "/zipDownload";
});
$('#remove').on('click', function() {
$.ajax({
url: '/remove',
method: 'POST',
data: {file: '1.bmp'},
type: 'POST',
dataType: 'TEXT',
success: function(msg) {
alert(msg);
}
});
});
$('#showImage').on('click', function() {
$('#body').append($('<img src="/showImage"/>'));
});
});
</script>
</body>
</html>