批量下载,多文件压缩打包zip下载
生活随笔
收集整理的這篇文章主要介紹了
批量下载,多文件压缩打包zip下载
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
0、寫在前面的話
圖片批量下載,要求下載時(shí)集成為一個(gè)壓縮包進(jìn)行下載。從昨天下午折騰到現(xiàn)在,踩坑踩得莫名其妙,還是來(lái)嘮嘮,給自己留個(gè)印象的同時(shí),也希望給需要用到這個(gè)方法的人帶來(lái)一些幫助。1、先叨叨IO
叨叨IO是因?yàn)榫W(wǎng)絡(luò)傳輸無(wú)非也就是流的傳遞,所以下載文件到本地的話實(shí)際上也是IO的東西,這個(gè)和讀取本地文件然后寫入到本地另一個(gè)文件的操作是基本一樣的。我在自己IO基礎(chǔ)的博客中(《[03] 節(jié)點(diǎn)流和處理流》)其實(shí)也有提到示例,拿復(fù)寫文件來(lái)說(shuō),大概是如下過(guò)程:對(duì)于讀取文件(不僅僅是文本)到服務(wù)器內(nèi)存,常見(jiàn)的是通過(guò)InputStream讀取File,所以你可能也經(jīng)??吹饺缦骂愃频拇a:outputStream = new FileOutputStream(file); byte[] temp = new byte[1024]; int size = -1; while ((size = inputStream.read(temp)) != -1) { // 每次讀取1KB,直至讀完outputStream.write(temp, 0, size); }1outputStream = new FileOutputStream(file); ? 2byte[] temp = new byte[1024];3int size = -1;4while ((size = inputStream.read(temp)) != -1) { // 每次讀取1KB,直至讀完5 ? ?outputStream.write(temp, 0, size);6}
我這里想說(shuō)的是,不論何種形式,只需要知道的是,在寫出之前,要獲取將寫出的數(shù)據(jù),這個(gè)數(shù)據(jù)常常是作為byte[ ]類型的。
同時(shí),因?yàn)槭菍懗龅奖镜匚募?#xff0c;所以這里圖示中的OutputStream無(wú)非應(yīng)該是使用FileOutputStream罷了。那么以此來(lái)類比網(wǎng)絡(luò)傳輸下載的話,OutputStream顯然就替換成響應(yīng)HttpServletResponse中的OutputStream就可以了。本質(zhì)都是輸出流,不同的類型決定你輸出的方式等。
2、再叨叨ajax
當(dāng)你把文件數(shù)據(jù)的二進(jìn)制放到了響應(yīng)的流中,也確實(shí)在響應(yīng)中返回了,可是瀏覽器就是不爭(zhēng)氣,不給面子,不啟動(dòng)下載。這個(gè)時(shí)候,你要看看,你發(fā)送請(qǐng)求的方式,是否采取了ajax請(qǐng)求。如下圖采用ajax請(qǐng)求資源,確實(shí)收到了流信息,但是反饋在瀏覽器上卻什么也沒(méi)發(fā)生:原因在于,ajax的返回值類型是json/text/html/xml類型,或者可以說(shuō)ajax的發(fā)送,接受都只能是string字符串,不能流類型,所以無(wú)法實(shí)現(xiàn)文件下載,強(qiáng)用會(huì)出現(xiàn)response沖突。但用ajax仍然可以獲得文件的內(nèi)容,該文件將被保留在內(nèi)存中,無(wú)法將文件保存到磁盤。這是因?yàn)閖s無(wú)法和磁盤進(jìn)行交互,否則這會(huì)是一個(gè)嚴(yán)重的安全問(wèn)題,js無(wú)法調(diào)用到瀏覽器的下載處理機(jī)制和程序,會(huì)被瀏覽器阻塞。
所以在前端,簡(jiǎn)單一點(diǎn),使用?window.location.href 的方式訪問(wèn)url,實(shí)現(xiàn)下載。
3、正兒八經(jīng)的批量下載實(shí)現(xiàn)
下載前,因?yàn)榕肯螺d需要打包為壓縮包,所以要用到一個(gè)三方j(luò)ar,maven地址如下:<dependency><groupId>ant</groupId><artifactId>ant</artifactId><version>1.6.5</version> </dependency>51<dependency>2 ?<groupId>ant</groupId>3 ?<artifactId>ant</artifactId>4 ?<version>1.6.5</version>5</dependency>先貼代碼,然后再進(jìn)行說(shuō)明:/*** 批量下載** @param idxs 圖片的id拼接字符串,用逗號(hào)隔開(kāi)*/ public String downloadBatch(String idxs) {String[] ids = idxs.split(",");try {HttpServletResponse response = ServletActionContext.getResponse();OutputStream out = setDownloadOutputStream(response, String.valueOf(new Date().getTime()), "zip");ZipOutputStream zipOut = new ZipOutputStream(out);for (int i = 0; i < ids.length; i++) {Picture picture = Picture.get(Picture.class, Long.parseLong(ids[i]));byte[] data = picture.getData();zipOut.putNextEntry(new ZipEntry(i + "_" + picture.getName() + "." + picture.getFormat().getValue()));writeBytesToOut(zipOut, data, BUFFER_SIZE);zipOut.closeEntry();}zipOut.flush();zipOut.close();} catch (IOException e) {e.printStackTrace();log.warn("下載失敗:" + e.getMessage());}return null; }x1/**2 * 批量下載3 *4 * @param idxs 圖片的id拼接字符串,用逗號(hào)隔開(kāi)5 */6public String downloadBatch(String idxs) {7 ? ?String[] ids = idxs.split(",");8 ? ?try {9 ? ? ? ?HttpServletResponse response = ServletActionContext.getResponse();10 ? ? ? ?OutputStream out = setDownloadOutputStream(response, String.valueOf(new Date().getTime()), "zip");11 ? ? ? ?ZipOutputStream zipOut = new ZipOutputStream(out);1213 ? ? ? ?for (int i = 0; i < ids.length; i++) {14 ? ? ? ? ? ?Picture picture = Picture.get(Picture.class, Long.parseLong(ids[i]));15 ? ? ? ? ? ?byte[] data = picture.getData();16 ? ? ? ? ? ?zipOut.putNextEntry(new ZipEntry(i + "_" + picture.getName() + "." + picture.getFormat().getValue()));17 ? ? ? ? ? ?writeBytesToOut(zipOut, data, BUFFER_SIZE);18 ? ? ? ? ? ?zipOut.closeEntry();19 ? ? ? }20 ? ? ? ?zipOut.flush();21 ? ? ? ?zipOut.close();2223 ? } catch (IOException e) {24 ? ? ? ?e.printStackTrace();25 ? ? ? ?log.warn("下載失敗:" + e.getMessage());26 ? }2728 ? ?return null;29}
/*** 設(shè)置文件下載的response格式** @param response 響應(yīng)* @param fileName 文件名稱* @param fileType 文件類型* @return 設(shè)置后響應(yīng)的輸出流OutputStream* @throws IOException*/ private static OutputStream setDownloadOutputStream(HttpServletResponse response, String fileName, String fileType) throws IOException {fileName = new String(fileName.getBytes(), "ISO-8859-1");response.setHeader("Content-Disposition", "attachment;filename=" + fileName + "." + fileType);response.setContentType("multipart/form-data");return response.getOutputStream(); }151/**2 * 設(shè)置文件下載的response格式3 *4 * @param response 響應(yīng)5 * @param fileName 文件名稱6 * @param fileType 文件類型7 * @return 設(shè)置后響應(yīng)的輸出流OutputStream8 * @throws IOException9 */10private static OutputStream setDownloadOutputStream(HttpServletResponse response, String fileName, String fileType) throws IOException {11 ? ?fileName = new String(fileName.getBytes(), "ISO-8859-1");12 ? ?response.setHeader("Content-Disposition", "attachment;filename=" + fileName + "." + fileType);13 ? ?response.setContentType("multipart/form-data");14 ? ?return response.getOutputStream();15}
/*** 將byte[]類型的數(shù)據(jù),寫入到輸出流中** @param out 輸出流* @param data 希望寫入的數(shù)據(jù)* @param cacheSize 寫入數(shù)據(jù)是循環(huán)讀取寫入的,此為每次讀取的大小,單位字節(jié),建議為4096,即4k* @throws IOException*/ private static void writeBytesToOut(OutputStream out, byte[] data, int cacheSize) throws IOException {int surplus = data.length % cacheSize;int count = surplus == 0 ? data.length / cacheSize : data.length / cacheSize + 1;for (int i = 0; i < count; i++) {if (i == count - 1 && surplus != 0) {out.write(data, i * cacheSize, surplus);continue;}out.write(data, i * cacheSize, cacheSize);} }1/**2 * 將byte[]類型的數(shù)據(jù),寫入到輸出流中3 *4 * @param out 輸出流5 * @param data 希望寫入的數(shù)據(jù)6 * @param cacheSize 寫入數(shù)據(jù)是循環(huán)讀取寫入的,此為每次讀取的大小,單位字節(jié),建議為4096,即4k7 * @throws IOException8 */9private static void writeBytesToOut(OutputStream out, byte[] data, int cacheSize) throws IOException {10 ? ?int surplus = data.length % cacheSize;11 ? ?int count = surplus == 0 ? data.length / cacheSize : data.length / cacheSize + 1;12 ? ?for (int i = 0; i < count; i++) {13 ? ? ? ?if (i == count - 1 && surplus != 0) {14 ? ? ? ? ? ?out.write(data, i * cacheSize, surplus);15 ? ? ? ? ? ?continue;16 ? ? ? }17 ? ? ? ?out.write(data, i * cacheSize, cacheSize);18 ? }19}
第一段代碼為下載的主方法,用到了兩個(gè)子方法,分別貼在之后的第二段代碼和第三段代碼。
文件的下載其實(shí)很簡(jiǎn)單,剛才在叨叨IO中也提到了,所以對(duì)于網(wǎng)絡(luò)傳輸下載的IO來(lái)說(shuō),整體也就三個(gè)步驟:
- 設(shè)置文件ContentType類型和文件頭
- 讀取文件數(shù)據(jù)為byte[]
- 將數(shù)據(jù)寫入到響應(yīng)response的輸出流中
設(shè)置請(qǐng)求頭信息,在方法?setDownloadOutputStream() 中已經(jīng)寫明了,是文件所以要告知文件處理應(yīng)該為 attachement,并附上文件名(轉(zhuǎn)碼ISO-8859-1避免中文亂碼)。而文件內(nèi)容的類型,統(tǒng)一設(shè)置為?multipart/form-data 即可,交給瀏覽器自行判斷下載的文件類型。
讀取文件數(shù)據(jù),因?yàn)楸纠形业膱D片數(shù)據(jù)直接存儲(chǔ)在數(shù)據(jù)庫(kù)字段中,所以取出時(shí)直接獲取的就是byte[]。如果你的方式是文件存放在本地,數(shù)據(jù)庫(kù)只是存儲(chǔ)了文件的物理地址,那么你得多做做操作,用FileInputStream把文件的內(nèi)容先讀出來(lái),結(jié)果和目的都是一樣的,就是獲取文件的byte[]。
獲得了數(shù)據(jù),那么直接通過(guò)OutputStream的write方法寫入即可。(這里的寫入方法我用了循環(huán)寫入,當(dāng)初是想著避免內(nèi)存緊張,可是現(xiàn)在回過(guò)頭來(lái)想不對(duì)啊,讀文件的時(shí)候才吃內(nèi)存的,這里我已經(jīng)讀完了再寫,循環(huán)與否已經(jīng)不重要了。所以實(shí)際上應(yīng)該是邊讀邊寫,才是良性的,可是我的byte[]存在數(shù)據(jù)庫(kù)字段里,取出來(lái)時(shí)就全部讀入到內(nèi)存中了,所以這里實(shí)際上是不需要循環(huán)寫入的,我這是畫(huà)蛇添足了。另外,如果是從File讀的話,則邊讀邊寫,見(jiàn)目錄1叨叨IO中的小段代碼)
寫入到輸出流了,flush()刷新一下,即可。
上面這些是對(duì)于文件下載通用的,如果是批量壓縮包形式,在第一段代碼中黃色部分,來(lái)進(jìn)行重點(diǎn)說(shuō)明:
- ZipOutputStream zipOut = new ZipOutputStream(out);?
- //把響應(yīng)的輸出流包裝一下而已,便于使用相關(guān)壓縮方法,就像多了個(gè)包裝袋
- zipOut.putNextEntry(new ZipEntry(i + "_" + picture.getName() + "." + picture.getFormat().getValue()));?
- zipOut.closeEntry();
- //壓縮包中的多個(gè)文件,實(shí)際上每個(gè)就是這里的ZipEntry對(duì)象,每開(kāi)始寫入某個(gè)文件的內(nèi)容時(shí),必須先putNextEntry(new ZipEntry(String fileName)),然后才可以寫入,寫完這個(gè)文件,必須使用closeEntry()說(shuō)明,已經(jīng)寫完了第一個(gè)文件。就好像putNextEntry是在說(shuō) “我要開(kāi)始寫壓縮包的下一個(gè)文件啦”,而closeEntry則是在說(shuō)“壓縮包里的這個(gè)文件我已經(jīng)寫完啦”。循環(huán)反復(fù),最終把所有文件寫入這個(gè)“披著壓縮輸出流外殼的響應(yīng)輸出流”
- zipOut.flush();
- 寫入完成后,刷一下即可。就像去超市買東西,購(gòu)物車裝好了,總得結(jié)一次帳才能把東西拿走吧。當(dāng)然,最后別忘了close關(guān)閉。
4、參考鏈接
- 不能用ajax請(qǐng)求下載文件
- 文件下載(只需要簡(jiǎn)單的四步),Java中都通用
- Java IO 壓縮流(ZipOutputStream/ZipInputStream)
轉(zhuǎn)載于:https://www.cnblogs.com/deng-cc/p/7809364.html
總結(jié)
以上是生活随笔為你收集整理的批量下载,多文件压缩打包zip下载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: pandas-事例练习
- 下一篇: springboot 找不到mapper