批量檔案打包下載
最近工作上有個需求,獲取批量檔案,打包下載,如果原始檔名中有重複,則需要重新命名(加角標),如果壓縮後文件超過20兆,出於寬頻考慮,不下載,但給與提示.
思路. 先獲取所有檔案,重新命名,再壓縮存起來再判斷大小,滿足大小寫出流,不滿足給與提示
話不多說,如下程式碼可以直接執行
package com.fisher.cloud.controller;
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody;
/** * @author Fisher */ @Controller @RequestMapping("/test") public class Test {
private static final Logger logger = LoggerFactory.getLogger(Test.class);
/** * 獲取目錄下面所有檔案並打包,壓縮成一個hello.zip包,如果被壓縮原始檔名字有重複,在增加下標,如果壓縮包大於20兆,出於對寬頻的考慮就不下載 * ,但給與提示. */ @RequestMapping(value = "/download", method = RequestMethod.GET) @ResponseBody public String downloadAttachment(HttpServletResponse response) throws IOException { //測試如下2個目錄下的檔案打包,兩個目錄一樣的是為了測試重新命名問題 File file1 = new File("D:\\IT\\參考資料\\檔案匯入匯出"); File file2 = new File("D:\\IT\\參考資料\\檔案匯入匯出"); File file3 = new File("D:\\IT\\參考資料\\檔案下載模板匯出方式");
// 獲取所有檔案 List<File> fileList1 = Arrays.asList(file1.listFiles()); List<File> fileList2 = Arrays.asList(file2.listFiles()); List<File> fileList3 = Arrays.asList(file3.listFiles()); List<File> totalFile = new ArrayList<>(); if (fileList1.size() > 0) { for (File f : fileList1) { totalFile.add(f); } } if (fileList2.size() > 0) { for (File f : fileList2) { totalFile.add(f); } } if (fileList3.size() > 0) { for (File f : fileList3) { totalFile.add(f); } } try { // 壓縮檔案 到"D:\IT\test",名字為 "hello.zip File zipFile = this.zipFiles(totalFile, "D:\\IT\\test", "hello.zip"); // 如果壓縮檔案大於20兆,返回提示 Integer maxSize = 20; if (maxSize * 1024 * 1024 < zipFile.length()) { return "壓縮包超過規定的20兆大小,限制下載"; } else { // 將檔案寫出 writeOut(response, zipFile); } } catch (Exception e) { e.printStackTrace(); } return null;
}
// 批量檔案壓縮成zip包 public File zipFiles(List<File> fileList, String zipPath, String zipName) throws IOException { // 如果被壓縮檔案中有重複,會重新命名 Map<String, String> namePathMap = getTransferName(fileList); File zipPathFile = new File(zipPath); // 資料夾不存在則建立 if (!zipPathFile.exists()) { zipPathFile.mkdirs(); } File zipFile = new File(zipPath + File.separator + zipName); if (!zipFile.exists()) { zipFile.createNewFile(); } ZipOutputStream zos = null; BufferedInputStream bis = null; try { // 存放的目標檔案 zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile.getPath()))); Set<String> keySet = namePathMap.keySet(); ZipEntry zipEntry = null; for (String key : keySet) { // key是檔名,value是path // 指定原始檔 File sourceFile = new File(namePathMap.get(key)); // 建立ZIP實體,並新增進壓縮包,指定壓縮檔案中的檔名 zipEntry = new ZipEntry(key); zos.putNextEntry(zipEntry); // 讀取待壓縮的檔案並寫進壓縮包裡 bis = new BufferedInputStream(new BufferedInputStream(new FileInputStream(sourceFile), 1024 * 10)); byte[] bufs = new byte[1024 * 10]; int read = 0; while ((read = (bis.read(bufs, 0, 1024 * 10))) != -1) { zos.write(bufs, 0, read); } if (bis != null) { bis.close(); } }
} catch (Exception e) { e.printStackTrace(); } finally { // 關閉流 if (bis != null) { bis.close(); } if (null != zos) { zos.close(); } } return zipFile;
}
/** * 將檔案寫出到流 【方法名】{方法的功能/動作描述} * @author Fisher * * */ private void writeOut(HttpServletResponse response, File zipFile) throws IOException { response.reset(); response.setContentType("application/zip"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + zipFile.getName()); OutputStream outputStream = response.getOutputStream(); FileInputStream fis = null; try { fis = new FileInputStream(zipFile.getPath()); int len = 0; byte[] buffer = new byte[1024]; while ((len = fis.read(buffer)) > 0) { outputStream.write(buffer, 0, len); } outputStream.flush(); } finally { if (null != fis) { fis.close(); } if (null != outputStream) { outputStream.close(); } } }
// 計算壓縮包如果已存在重複的名稱,則在重複檔案後面跟上數字 如: 檔案(1).doc,檔案(2).doc public Map<String, String> getTransferName(List<File> fileList) { if (fileList == null || fileList.size() == 0) { return new HashMap<String, String>(); } // key存放檔名,value存放path Map<String, String> fileNameMap = new HashMap<>(); List<String> fileNames = new ArrayList<>(); for (File file : fileList) { // 獲取檔名 String fileName = file.getName(); int count = 0;
for (String name : fileNames) { if (name != null && name.equals(fileName)) { count++; } } fileNames.add(fileName); if (count > 0) { int lastIndex = fileName.lastIndexOf('.'); String name = fileName.substring(0, lastIndex); String type = fileName.substring(lastIndex + 1, fileName.length()); fileName = new StringBuilder().append(name).append("(").append(count).append(")").append(".") .append(type).toString(); fileNameMap.put(fileName, file.getPath()); } else { fileNameMap.put(fileName, file.getPath()); } } return fileNameMap; }
}
測試結果:相同名字會重複命名
超過20兆不下載,給與提示