需求:從ftp服務器打包下載文件數據庫
解決步驟:1.從ftp服務器把各個文件下載到本地服務器(通常是安裝tomcat的服務器,項目本身電腦跑的本地服務器就是本身電腦)指定目錄中瀏覽器
2.在本地服務器打包下載好的文件夾打包,返回打包好的File ziptomcat
3.zip文件用流寫入reponse,達到用戶下載效果服務器
準備文件:app
// 封裝全部須要打包下載的文件地址請求類 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; }
第二步:ui
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(); }
最後:spa