需求:从ftp服务器打包下载文件

解决步骤:1.从ftp服务器把各个文件下载到本地服务器(一般是安装tomcat的服务器,项目自己电脑跑的本地服务器就是自己电脑)指定目录中

2.在本地服务器打包下载好的文件夹打包,返回打包好的File zip

3.zip文件用流写入reponse,达到用户下载效果

准备文件:

// 封装所有需要打包下载的文件地址请求类
public class DownloadPackageReq implements Serializable {
// 本地服务器临时存放目录名(尽量唯一.eg:"menutree20200904112125")
private String localTempDirName; // 打包下载本地服务器文件夹名字
private List<DownloadPackageListReq> downloadPackageListReqList; // 需要下载所有文件路径和名称
}
// FTPClientUtils:ftp工具类
public static FTPClientUtils init() {
FTPClientUtils ftp = new FTPClientUtils();
ftp.setHost(host);
ftp.setPort(port);
ftp.setUsername(username);
ftp.setPassword(password);
ftp.setBinaryTransfer(true);
ftp.setPassiveMode(false);
ftp.setEncoding("utf-8");
return ftp;
}
/**
* 下载一个远程文件到本地的指定文件
*
* @param remoteAbsoluteFile
* 远程文件名(包括完整路径,eg:/MTL/test/menutree_attachment/file.xlsx)
* @param localAbsoluteFile
* 本地文件名(包括完整路径)
* @param autoClose
* 是否自动关闭当前连接
*
* @return 成功时,返回true,失败返回false
* @throws Exception
*/
public boolean get(String remoteAbsoluteFile, String localAbsoluteFile, boolean autoClose) throws Exception {
OutputStream output = null;
try {
output = new FileOutputStream(localAbsoluteFile);
return get(remoteAbsoluteFile, output, autoClose);
} catch (FileNotFoundException e) {
throw new Exception("local file not found.", e);
} finally {
try {
if (output != null) {
output.close();
}
} catch (IOException e) {
throw new Exception("Couldn't close FileOutputStream.", e);
}
}
}
/**
* 下载一个远程文件到指定的流 处理完后记得关闭流
*
* @param remoteAbsoluteFile
* @param output
* @param autoClose
* @return
* @throws Exception
*/
public boolean get(String remoteAbsoluteFile, OutputStream output, boolean autoClose) throws Exception {
try {
FTPClient ftpClient = getFTPClient();
// 处理传输
return ftpClient.retrieveFile(remoteAbsoluteFile, output);
} catch (IOException e) {
throw new Exception("Couldn't get file from server.", e);
} finally {
if (autoClose) {
disconnect(); // 关闭链接
}
}
}


第一步:

public File downloadMenuTreeAttachment(Integer menutreeId) throws Exception {
// 从数据库拿到menutreeId对应的所有文件地址
List<ResourcesMenutreeListVo> resourcesMenutreeLists = resourcesMenutreeListMapper.selExistingAttachment(menutreeId);
DownloadPackageReq req = new DownloadPackageReq();
if (CollectionUtils.isNotEmpty(resourcesMenutreeLists)) {
req.setLocalTempDirName(resourcesMenutreeLists.get(0).getMenutreeName() + DateUtils.dateTimeNow());
List<DownloadPackageListReq> dpList = new ArrayList<>();
for(ResourcesMenutreeListVo temp : resourcesMenutreeLists) {
DownloadPackageListReq dpReq = new DownloadPackageListReq();
// 文件名称,用来下载ftp服务器文件修改文件名(因为ftp文件都是uuid名称)
String fileName = temp.getModuleName() + "_" + temp.getEngineerName();
dpReq.setFileName(fileName);
dpReq.setFileFtpUrl(temp.getMenutreeAttachment());
dpList.add(dpReq);
}
req.setDownloadPackageListReqList(dpList);
} else {
req.setLocalTempDirName("空菜单树" + DateUtils.dateTimeNow());
}
return ftpService.zipFiles(req);
}
public File zipFiles(DownloadPackageReq req) throws Exception {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 本地服务器暂存路径
String localTempDir = request.getSession().getServletContext().getRealPath("/") + req.getLocalTempDirName() + File.separator;
logger.info("本地服务器暂存路径:" + localTempDir);
File dir = new File(localTempDir);
if ( ! dir.exists()) {
dir.mkdir();
}
if (CollectionUtils.isNotEmpty(req.getDownloadPackageListReqList())) {
List<DownloadPackageListReq> downloadList = req.getDownloadPackageListReqList();
FTPClientUtils ftp = FTPClientUtils.init();
for(int i=0; i<downloadList.size(); i++) {
// 是否关闭传输流(最后一份文件传输完毕关闭)
boolean isCloseStream = false;
if (i == downloadList.size() - 1) {
isCloseStream = true;
}
DownloadPackageListReq temp = downloadList.get(i);
String fileFtpUrl = temp.getFileFtpUrl();
String suffix = "";
if (fileFtpUrl.contains(".") && !fileFtpUrl.endsWith(".")) {
// 文件后缀名
suffix = fileFtpUrl.substring(fileFtpUrl.lastIndexOf("."));
}
// 本地服务器存放完整路径(包含文件名)
String localUrl = localTempDir + temp.getFileName() + suffix;
// 下载的第一个参数远程路径如果是FTP服务器,就采用服务器完整文件路径(eg:/MTL/test/menutree_attachment/file.xlsx),因为初始化ftp服务器就带了ip端口
boolean result = ftp.get(temp.getFileFtpUrl(), localUrl, isCloseStream);
}
}
// 打包下载好的文件
File zipFile = ZipUtil.zip(localTempDir, localTempDir + req.getLocalTempDirName() + ".zip");
return zipFile;
}


第二步:

public class ZipUtil {
private static Logger logger = Logger.getLogger(ZipUtil.class);

/**
* 缓冲器大小
*/
private static final int BUFFER = 512;

/**
* 压缩方法 (可以压缩空的子目录)
*
* @param srcPath 压缩源路径
* @param zipFileName 目标压缩文件
* @return
*/
public static File zip(String srcPath, String zipFileName) {
ZipOutputStream zipOutputStream = null;
InputStream inputStream = null;
File outputZipFile = null;
try {
// 检查文件是否存在,是的话先删除
outputZipFile = new File(zipFileName);
if (outputZipFile.exists()) {
outputZipFile.delete();
}

File srcFile = new File(srcPath);
List<File> fileList = FileUtil.getAllFiles(srcFile);// 所有要压缩的文件
byte[] buffer = new byte[BUFFER];// 缓冲器
ZipEntry zipEntry = null;
int readLength = 0;// 每次读出来的长度
zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFileName));

for (File file : fileList) {
if (file.isFile()) {// 若是文件,则压缩这个文件
zipEntry = new ZipEntry(getRelativePath(srcPath, file));
zipEntry.setSize(file.length());
zipEntry.setTime(file.lastModified());
zipOutputStream.putNextEntry(zipEntry);

inputStream = new BufferedInputStream(new FileInputStream(file));

while ((readLength = inputStream.read(buffer, 0, BUFFER)) != -1) {
zipOutputStream.write(buffer, 0, readLength);
}
} else {// 若是目录(即空目录)则将这个目录写入zip条目
zipEntry = new ZipEntry(getRelativePath(srcPath, file) + File.separator);
zipOutputStream.putNextEntry(zipEntry);
}
} // end for
} catch (FileNotFoundException e) {
logger.error("zip fail!", e);
} catch (IOException e) {
logger.error("zip fail!", e);
} finally {
close(inputStream);
close(zipOutputStream);
}
// 返回文件输出流
outputZipFile = new File(zipFileName);
return outputZipFile;
}

/**
* 关闭流
*/
private static void close(Closeable c) {
if (c == null)
return;
try {
c.close();
} catch (IOException e) {
logger.error("close fail!", e);
}
c = null;
}

/**
* 取相对路径 依据文件名和压缩源路径得到文件在压缩源路径下的相对路径
*
* @param dirPath 压缩源路径
* @param file
* @return 相对路径
*/
public static String getRelativePath(String dirPath, File file) {
File dir = new File(dirPath);
String relativePath = file.getName();
while (true) {
file = file.getParentFile();
if (file == null) {
break;
}
if (file.equals(dir)) {
break;
} else {
relativePath = file.getName() + "/" + relativePath;
}
} // end while
return relativePath;
}
}


第三步:Controller控制器,Result是自己封装的返回类,可以自定义String之类的返回

public Result downloadMenuTreeAttachment(Integer menutreeId, HttpServletResponse response) {
BufferedInputStream bis = null;
OutputStream os = null;
try {
File file = resourcesMenutreeListService.downloadMenuTreeAttachment(menutreeId);
response.reset();
response.setCharacterEncoding("utf-8");
response.setContentLength((int) file.length());
// 设置content-disposition响应头控制浏览器以下载的形式打开文件,中文文件名要使用URLEncoder.encode方法进行编码,否则会出现文件名乱码
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
bis = new BufferedInputStream(new FileInputStream(file));
os = response.getOutputStream();
byte[] buff = new byte[1024];
int i = 0;
while ((i = bis.read(buff)) != -1) {
os.write(buff, 0, i);
os.flush();
}
} catch (Exception e) {
log.error("{}",e);
return ResultGenerator.genFailResult("下载失败");
} finally {
try {
bis.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return ResultGenerator.genSuccessResult();
}


最后: