java批量下載將多個(gè)文件(minio中存儲(chǔ))壓縮成一個(gè)zip包代碼示例
我的需求是將minio中存儲(chǔ)的文件按照查詢條件查詢出來(lái)統(tǒng)一壓成一個(gè)zip包然后下載下來(lái)。
思路:
針對(duì)這個(gè)需求,其實(shí)可以有多個(gè)思路,不過(guò)也大同小異,一般都是后端返回流文件前端再處理下載,也有少數(shù)是壓縮成zip包之后直接給下載鏈接返回到前端,前端收到鏈接url直接window.open()進(jìn)行下載,不過(guò)這種下載zip包的路徑要確保是在網(wǎng)站下,否則訪問(wèn)不到,還有一個(gè)缺點(diǎn)就是文件沒(méi)法刪除,占用存儲(chǔ)空間,后期需人為動(dòng)作清理,選擇哪種思路就可以看具體需求啦,我選擇的是第一種思路,以下就針對(duì)第一種后端返回流方式進(jìn)行具體介紹。
首先說(shuō)第一種方法:
將需要下載的文件找到,minio中有查詢方法將文件轉(zhuǎn)成inputStream,這里就不多說(shuō)了,拿到一組InputStream,我們就可以寫(xiě)入一個(gè)zip包里了,創(chuàng)建臨時(shí)zip路徑,將流遍歷寫(xiě)入文件,讀取臨時(shí)zip文件再寫(xiě)入response中的outputStream,最后刪除臨時(shí)文件。
前端處理方法最后統(tǒng)一介紹,后端核心代碼如下:
public void downloadZip(String name, List<MediaFileEntity> filePaths,HttpServletResponse response){
File zipFile = compressedFileToZip(name,filePaths);
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
FileInputStream ins = new FileInputStream(zipFile);
WritableByteChannel writableByteChannel = Channels.newChannel(os);
FileChannel fileChannel = ins.getChannel();
fileChannel.transferTo(0, fileChannel.size(), writableByteChannel);
fileChannel.close();
response.setCharacterEncoding("UTF-8");
name = URLEncoder.encode(name, "UTF-8");
response.setContentType("application/octet-stream");
response.addHeader("Content-Disposition", "attachment;filename=" + new String(name.getBytes("iso8859-1")));
response.setContentLength(os.size());
response.setHeader("filename", name);
response.addHeader("Content-Length", "" + os.size());
var outputstream = response.getOutputStream();
os.writeTo(outputstream);
os.flush();
os.close();
outputstream.flush();
outputstream.close();
writableByteChannel.close();
if(zipFile.exists()){
//刪除臨時(shí)文件
zipFile.delete();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 構(gòu)建臨時(shí)zip文件
**/
public File compressedFileToZip(String name, List<MediaFileEntity> mediaFileEntityList) {
String zipName = name.concat(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))).concat(".zip");
//臨時(shí)zip路徑
String fileZipPath = System.getProperty("user.dir").concat("/").concat(zipName);
OutputStream os = null;
ZipOutputStream zos = null;
File file = new File(fileZipPath);
try {
if(!file.exists()){
file.createNewFile();
}
os= new FileOutputStream(file);
zos = new ZipOutputStream(os) ;
for (MediaFileEntity entity:mediaFileEntityList
) {
zos.putNextEntry(new ZipEntry(entity.getFileName()));
//minio 獲取流
InputStream ins = ossService.getObject(OssConfiguration.bucket,entity.getObjectKey());
FileInputStream insf=convertToFileInputStream(ins);
WritableByteChannel writableByteChannel = Channels.newChannel(zos);
FileChannel fileChannel = insf.getChannel();
fileChannel.transferTo(0, fileChannel.size(), writableByteChannel);
zos.closeEntry();
fileChannel.close();
ins.close();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(zos != null){
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return file;
}第二種方法:
安裝hutool依賴,調(diào)用hutool包中的ZipUtil工具類(lèi)中的zip方法進(jìn)行下載,。此方法需要有3個(gè)參數(shù),分別是OutoutStream,每個(gè)流對(duì)應(yīng)的文件名字符串?dāng)?shù)組,文件的InputStream數(shù)組。

首先將需要下載的文件找到,拿到一組InputStream,也就是zip方法中的第3個(gè)參數(shù),第一個(gè)參數(shù)顧名思義就是你想要輸出的地方,我們是返回給前端所以就是response.getOutputStream(),第二個(gè)參數(shù)我們遍歷文件時(shí)也可以拿到,廢話不多說(shuō)了,上代碼看吧。
在項(xiàng)目下安裝hutool依賴
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.7</version>
</dependency> /**
* 下載多個(gè)文件轉(zhuǎn)zip壓縮包
*
* @param mediaFileEntityList
* @param response
* @throws Exception
*/
public void dowloadToZip(List<MediaFileEntity> mediaFileEntityList, HttpServletResponse response) throws Exception {
int i = 0;
//如果有附件 進(jìn)行zip處理
if (mediaFileEntityList != null && mediaFileEntityList.size() > 0) {
try {
//被壓縮文件流集合
InputStream[] srcFiles = new InputStream[mediaFileEntityList.size()];
//被壓縮文件名稱
String[] srcFileNames = new String[mediaFileEntityList.size()];
for (MediaFileEntity entity : mediaFileEntityList) {
//以下代碼為獲取圖片inputStream
InputStream ins = ossService.getObject(OssConfiguration.bucket,entity.getObjectKey());
if (ins == null) {
continue;
}
//塞入流數(shù)組中
srcFiles[i] = ins;
srcFileNames[i] = entity.getFileName();
i++;
}
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("下載.zip", "UTF-8"));
//多個(gè)文件壓縮成壓縮包返回
ZipUtil.zip(response.getOutputStream(), srcFileNames, srcFiles);
} catch (IOException e) {
e.printStackTrace();
}
}
}Controller這邊可以直接寫(xiě)成沒(méi)有返回值的接口,我的例子如下,僅供參考:
@GetMapping("/{workspace_id}/fileDownList")
@ApiOperation(value = "查詢文件的下載地址")
public void getFileStreamList(@PathVariable(name = "workspace_id") String workspaceId,
@RequestParam(name = "ids") String ids, HttpServletResponse response) throws Exception {
List<Integer> fileIds= Arrays.stream(ids.split(",")).collect(Collectors.toList()).stream()
.map(Integer::parseInt)
.collect(Collectors.toList());
List<MediaFileEntity> mediaFileEntityList = fileService.getMediaListById(workspaceId, fileIds);
// fileUtil.downloadZip("111",mediaFileEntityList,response);//第一種方法
fileUtil.dowloadToZip(mediaFileEntityList,response);//第二種方法
}到此,后端zip下載就完畢了,下面我們說(shuō)說(shuō)前端如何處理
網(wǎng)上查詢前端處理大致都是如下,但是我自己使用的時(shí)候下載總是提示損壞,后找了一個(gè)工具類(lèi)直接調(diào)用,就可以了,示例代碼請(qǐng)求是get,如需調(diào)整,可根據(jù)情況自行調(diào)整
核心代碼如下:
import { saveAs } from 'file-saver';
const baseURL = (window as any).config.VITE_APP_BASE_API; //import.meta.env.VITE_APP_BASE_API;
export default {
zip(url: string, name: string) {
url = baseURL + url;
axios({
method: 'get',
url: url,
responseType: 'blob',
headers: { Authorization: 'Bearer ' + getToken() },
}).then(res => {
const isBlob = blobValidate(res.data);
if (isBlob) {
const blob = new Blob([res.data], { type: 'application/zip' });
this.saveAs(blob, name);
} else {
this.printErrMsg(res.data);
}
});
},
saveAs(text: any, name: string, opts?: any) {
saveAs(text, name, opts);
},
async printErrMsg(data: any) {
const resText = await data.text();
const rspObj = JSON.parse(resText);
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
// ElMessage.error(errMsg);
},
blobValidate(data: any) {
return data.type !== 'application/json';
}
};按鈕綁定方法直接調(diào)用zip下載方法,傳參為url和要導(dǎo)出zip的名稱,示例如下:
import download from '@/plugins/download';
function batchDownload(){
ElMessage.success("文件下載中,請(qǐng)勿重復(fù)點(diǎn)擊!");
download.zip(`/media/api/v1/files/${workspaceId}/fileDownList?ids=${selectlist.value.join(",")}`,"MediaFiles"+new Date().toLocaleDateString()+".zip")
}總結(jié)
到此這篇關(guān)于java批量下載將多個(gè)文件(minio中存儲(chǔ))壓縮成一個(gè)zip包的文章就介紹到這了,更多相關(guān)java批量下載多文件壓縮成zip包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring MVC攔截器(Interceptor)的定義和配置過(guò)程
這篇文章主要介紹了Spring MVC攔截器(Interceptor)的定義和配置過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
Java 實(shí)戰(zhàn)練手項(xiàng)目之醫(yī)院預(yù)約掛號(hào)系統(tǒng)的實(shí)現(xiàn)流程
讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SpringBoot+Maven+Vue+mysql實(shí)現(xiàn)一個(gè)醫(yī)院預(yù)約掛號(hào)系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-11-11
Spring Cloud 配置中心多環(huán)境配置bootstrap.yml的實(shí)現(xiàn)方法
spring cloud用上了配置中心,就一個(gè)boostrap.yml,本文就來(lái)介紹一下Spring Cloud 配置中心多環(huán)境配置bootstrap.yml的實(shí)現(xiàn)方法,感興趣的可以了解一下2024-03-03
SpringBoot項(xiàng)目啟動(dòng)錯(cuò)誤:找不到或無(wú)法加載主類(lèi)的幾種解決方法
本文主要介紹了SpringBoot項(xiàng)目啟動(dòng)錯(cuò)誤:找不到或無(wú)法加載主類(lèi)的幾種解決方法,具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03
SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況
這篇文章主要介紹了SpringBoot如何統(tǒng)一處理返回結(jié)果和異常情況問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
Druid數(shù)據(jù)庫(kù)連接池監(jiān)控使用及說(shuō)明
Druid是Java數(shù)據(jù)庫(kù)連接池,具有監(jiān)控和擴(kuò)展功能,性能好,自帶監(jiān)控頁(yè)面,支持密碼加密、SQL執(zhí)行日志等,Druid已經(jīng)在阿里巴巴部署了超過(guò)600個(gè)應(yīng)用2025-12-12

