最近工作上有個需求,獲取批量文件,打包下載,如果源文件名中有重複,則需要重命名(加角標),如果壓縮後文件超過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);
@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兆不下載,給與提示