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) {// 处理异常}}