java批量下載將多個(gè)文件(minio中存儲(chǔ))壓縮成一個(gè)zip包代碼示例
我的需求是將minio中存儲(chǔ)的文件按照查詢條件查詢出來統(tǒng)一壓成一個(gè)zip包然后下載下來。
思路:
針對(duì)這個(gè)需求,其實(shí)可以有多個(gè)思路,不過也大同小異,一般都是后端返回流文件前端再處理下載,也有少數(shù)是壓縮成zip包之后直接給下載鏈接返回到前端,前端收到鏈接url直接window.open()進(jìn)行下載,不過這種下載zip包的路徑要確保是在網(wǎng)站下,否則訪問不到,還有一個(gè)缺點(diǎn)就是文件沒法刪除,占用存儲(chǔ)空間,后期需人為動(dòng)作清理,選擇哪種思路就可以看具體需求啦,我選擇的是第一種思路,以下就針對(duì)第一種后端返回流方式進(jìn)行具體介紹。
首先說第一種方法:
將需要下載的文件找到,minio中有查詢方法將文件轉(zhuǎn)成inputStream,這里就不多說了,拿到一組InputStream,我們就可以寫入一個(gè)zip包里了,創(chuàng)建臨時(shí)zip路徑,將流遍歷寫入文件,讀取臨時(shí)zip文件再寫入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工具類中的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í)也可以拿到,廢話不多說了,上代碼看吧。
在項(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這邊可以直接寫成沒有返回值的接口,我的例子如下,僅供參考:
@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下載就完畢了,下面我們說說前端如何處理
網(wǎng)上查詢前端處理大致都是如下,但是我自己使用的時(shí)候下載總是提示損壞,后找了一個(gè)工具類直接調(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)文章
JAVA求兩直線交點(diǎn)和三角形內(nèi)外心的方法
本文提供了JAVA求兩直線交點(diǎn)、三角形外心、三角形內(nèi)心的代碼和算法講解,大家可以參考使用2013-11-11Java高并發(fā)系統(tǒng)限流算法的實(shí)現(xiàn)
這篇文章主要介紹了Java高并發(fā)系統(tǒng)限流算法的應(yīng)用,在開發(fā)高并發(fā)系統(tǒng)時(shí)有三把利器用來保護(hù)系統(tǒng):緩存、降級(jí)和限流,限流可以認(rèn)為服務(wù)降級(jí)的一種,限流是對(duì)系統(tǒng)的一種保護(hù)措施,需要的朋友可以參考下2022-05-05Java 圖片復(fù)制功能實(shí)現(xiàn)過程解析
這篇文章主要介紹了Java 圖片復(fù)制功能實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10mybatis實(shí)現(xiàn)增刪改查_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
本文通過實(shí)例代碼給大家介紹了mybatis實(shí)現(xiàn)增刪改查功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-09-09java 靜態(tài)工廠代替多參構(gòu)造器的適用情況與優(yōu)劣
這篇文章主要介紹了java 靜態(tài)工廠代替多參構(gòu)造器的優(yōu)劣,幫助大家更好的理解和使用靜態(tài)工廠方法,感興趣的朋友可以了解下2020-12-12