1.业务背景
公司想实现文件系统下载,上次图简单就草率的写了文件下载,这不趁着同事请假赶集吧这坑给填上。
2.遇到问题
刚准备开始写,就头疼,文件只要获得数据输出流就行,但是这文件夹需要维护层级关系。
前端给的是服务器的绝对地址,还得服务器的文件名对的上,不然的下载个1.doc 2.txt 3.image,另外得考虑到如果文件夹下面的文件路径不存在会不会报错
因为每个文件夹的目录的和流关联起来 ,而且我们只知道下载的文件地址。所以在打成zip包的时候得考虑地址替换以及子文件夹创建的问题。
3.解决思路
解决方法很简单,但可能一时半会给绕进去了,都想半天了。
关于层级的问题:先去校验是否是文件进行递归操作 没啥难度
关于路径的问题:使用下载路径作为根目录,是文件的时候将目录进行传递,也无需在打成zip包在做解析
关于目录的问题:需要做标识,文件放的是字节数组,文件夹放的是null,在对应层级创建目录即可。
4.代码实现(展现ftp的实现方式)
1.下载
/**
* 批量下载文件或者文件夹
*
* @param host 服务器地址
* @param port 端口号
* @param username 用户名
* @param password 密码
* @param downPaths 远程路徑
* @return void
*/
public Map<String, byte[]> batchDownloadFilesOrFolder(String host, int port, String username, String password, List<String> downPaths) throws IOException {
FTPClient ftp = getFtpClient(host, port, username, password);
Map<String, byte[]> fileBytesMap = new HashMap<>();
for (String downPath : downPaths) {
// 检查下载路径是否为文件夹
String rootPath = StringUtils.getFileName(downPath);
FTPFile ftpFile = ftp.mlistFile(downPath);
if (ftpFile != null && ftpFile.isDirectory()) {
fileBytesMap.put(rootPath , null);
// 如果是文件夹,则递归调用下载文件夹的方法
downloadDirectory(ftp, downPath, rootPath, fileBytesMap);
} else {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
if (!ftp.retrieveFile(downPath, outputStream)) {
// 文件下载失败的处理逻辑,比如抛出异常或记录日志
} else {
fileBytesMap.put(rootPath, outputStream.toByteArray());
}
}
}
return fileBytesMap;
}
/**
* 下载文件夹的方法
*
* @param ftp
* @param directoryPath
* @param rootPath 根目录
* @param fileBytesMap
* @throws IOException
*/
private void downloadDirectory(FTPClient ftp, String directoryPath, String rootPath, Map<String, byte[]> fileBytesMap) throws IOException {
FTPFile[] files = ftp.listFiles(directoryPath);
for (FTPFile file : files) {
String filePath = directoryPath + "/" + file.getName();
String relativePath = rootPath + "/" + file.getName();
if (file.isFile()) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
if (!ftp.retrieveFile(filePath, outputStream)) {
// 文件下载失败的处理逻辑,比如抛出异常或记录日志
} else {
fileBytesMap.put(relativePath, outputStream.toByteArray());
}
} else if (file.isDirectory()) {
// 递归调用下载文件夹的方法
fileBytesMap.put(relativePath, null);
downloadDirectory(ftp, filePath, relativePath, fileBytesMap);
}
}
}
2.打成zip包
/**
* 将下载的流写入zip
*
* @param bytesMap
* @param response
*/
private void downStreamWriteZip(Map<String, byte[]> bytesMap, HttpServletResponse response){
try {
// 设置响应头
response.setHeader("Content-Disposition", "attachment; filename=files.zip");
response.setContentType("application/zip");
// 创建输出流
ServletOutputStream outputStream = response.getOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
for (Map.Entry<String, byte[]> entry : bytesMap.entrySet()) {
String fileName = entry.getKey();
byte[] data = entry.getValue();
if (data == null) {
// 文件夹,需要创建目录结构
ZipEntry zipEntry = new ZipEntry(fileName + "/");
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.closeEntry();
} else {
// 文件,写入文件数据
ZipEntry zipEntry = new ZipEntry(fileName);
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.write(data);
zipOutputStream.closeEntry();
}
}
// 关闭流
zipOutputStream.close();
outputStream.close();
} catch (IOException e) {
// 处理异常
}
}