FTP服务器 打包 下载文件
需求:从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(); }
最后:
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
你不知道的 GraphQL
本文由 kazaff 翻译而成,点击阅读原文可以查看作者的博客,感谢作者的优质输出,让我们的技术世界更加美好✌️ 很久之前其实就关注过这个技术,记得当时还是React刚刚崭露头角的时期吧。总之那时候,GraphQL感觉还只是概念完备阶段,除了FB自己内部大量使用外,好像社区并不是很健全,不过大家应该都在疯狂的讨论和跟进吧。过了2年,如今再回过头来看,已经涌现出各种开源或商用服务专注于这个领域,各种语言的框架和工具也都很完备了,感觉是时候重新接触GraphQL了。如果你的项目正处于技术选型,你正在犹豫选择一种接口风格的时刻,不妨了解一下这个神奇而强大的玩意儿~~ 本文打算翻译一篇感觉很解惑的文章,主要围绕着GraphQL的server端实现,因为相比client端,server端包含了更多的内容。后面如果有机会,也会尝试提供关于client端相关的内容,不过前端同学可以先看一下这里:howtographql[1],这里有各种最佳实践,应该总会找到和你正在使用相关的前端框架的整合方案,好像有个对应的中文版[2]~ 关于GraphQL概念的内容,这篇文章并没有涉及太多,不过假如你用搜索引擎去...
- 下一篇
架构师之路(十一)之探讨一台机器中JVM能创建的线程上限到底是多大?
引言 这两天在用多线程ThreadPoolExecutor解决问题的时候,突发奇想的了解一下jvm到底最多能创建多少线程,因为在遇到高并发业务场景的时候,必须使用多线程来应付问题,正所谓兵来将挡,水来土掩,业务请求来自然就是线程干活了.了解一下影响jvm创建线程的因素对后续jvm调优,高并发问题的解决多多少会有点帮助吧,,哪怕一点. JVM 体系结构 要想了解jvm对线程的影响,首先得简单了解一下jvm的体系结构,这里直接上图: jvm的基本结构图 上图是从网上直接扒下来的,其实都差不多,简单介绍一下; (1) 程序计数器: 这玩意又叫PC寄存器, 程序计数器是线程私有的内存,JVM多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,当线程切换后需要恢复到正确的执行位置(处理器)时,就是通过程序计数器来实现的。此内存区域是唯一 一个在JVM规范中没有规定任何OutOfMemoryError情况的区域。 (2) Java虚拟机栈: Java虚拟机栈也是线程私有的,它的生命周期与线程相同,Java虚拟机栈为JVM执行的Java方法(字节码)服务。每个Java方法...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS关闭SELinux安全模块
- Red5直播服务器,属于Java语言的直播服务器
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Thymeleaf,官方推荐html解决方案