1. 程式人生 > 實用技巧 >下載https檔案並打包成ZIP

下載https檔案並打包成ZIP

首先Controller層程式碼

 1 @RequestMapping(value = "downloadZipFile")
 2     @OptionsLog(optDesc = "打包影印件下載", optType = "返現日誌")
 3     public void downloadZipFile(HttpServletResponse response, String id) throws IOException {
 4         String zipName = "myfile.zip";
 5         response.setContentType("APPLICATION/OCTET-STREAM");
6 response.setHeader("Content-Disposition", "attachment; filename=" + zipName); 7 ZipOutputStream out = new ZipOutputStream(response.getOutputStream()); 8 try { 9 List<BsOssUpload> bsOssUploadList = recurrenceService.getFileList(id); 10 for (Iterator<BsOssUpload> it = bsOssUploadList.iterator(); it.hasNext(); ) {
11 BsOssUpload file = it.next(); 12 ZipUtils.doZip(file.getFileName(), file.getFileUrl(), out); 13 response.flushBuffer(); 14 } 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } finally { 18 out.close();
19 } 20 }
ZipEntry是zip下面的檔案條目,你可以比作外面系統的File類似。
後面的引數就是在zip目錄下的相對位置。
所以這裡有一點比較重要的就是當你遍歷資料夾的時候你的ZipEntry的引數的改變規律。

寫不好的話會使整個檔案目錄混亂(如果檔案層級較低那就不礙事)。
而putNextEntry(ZipEntry z)的意思就是我下面io操作(寫入)都是在z這個檔案條目下進行的。
zipoutputstream流和其他的output流不一樣的地方就是BufferedOutputStream不能巢狀它。
也就是它不能套快取流用。對於資料夾下包含資料夾需要特殊考慮。判斷它是不是資料夾。

資料夾的話要遍歷他的子節點檔案。用遞迴思想。已在程式碼中給出註釋。還有檔案要注意相對絕對路徑。
package com.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.DataInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author 孫正揚
 * @date 2020-08-04 17:21:22
 */
public class ZipUtils {

    private static Logger logger = LoggerFactory.getLogger(ZipUtils.class);

    /**
     * 獲取https路徑的檔案打包成zip檔案
     *
     * @param httpUrl
     * @param out
     */
    public static void doZip(String fileName, String httpUrl, ZipOutputStream out) {
        InputStream in = null;
        File file = null;
        try {

            if (httpUrl.startsWith("https://")) {
                /**
                 * 設定忽略ssl證書
                 */
                SSLContext sslcontext = null;
                sslcontext = SSLContext.getInstance("SSL", "SunJSSE");
                sslcontext.init(null, new TrustManager[]{new X509TrustUtiil()}, new java.security.SecureRandom());
                HostnameVerifier ignoreHostnameVerifier = new myHostnameVerifier();
                URL url = new URL(httpUrl);
                HttpsURLConnection.setDefaultHostnameVerifier(ignoreHostnameVerifier);
                HttpsURLConnection.setDefaultSSLSocketFactory(sslcontext.getSocketFactory());
                HttpsURLConnection urlCon = (HttpsURLConnection) url.openConnection();
                urlCon.setConnectTimeout(6000);
                urlCon.setReadTimeout(6000);
                int code = urlCon.getResponseCode();
                if (code != HttpURLConnection.HTTP_OK) {
                    try {
                        throw new Exception("檔案讀取失敗");
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                /**
                 * ZipEntry是zip下面的檔案條目,你可以比作外面系統的File類似。
                 * 後面的引數就是在zip目錄下的相對位置。所以這裡有一點比較重要的就是當你遍歷資料夾的時候你的ZipEntry的引數的改變規律。
                 * 寫不好的話會使整個檔案目錄混亂(如果檔案層級較低那就不礙事)。
                 * 而putNextEntry(ZipEntry z)的意思就是我下面io操作(寫入)都是在z這個檔案條目下進行的。
                 */
                String[] zips = httpUrl.split("/");
                String[] lastName = httpUrl.split("\\.");
                ZipEntry entry = new ZipEntry(fileName == null ? zips[zips.length - 1] : fileName + "." + lastName[lastName.length - 1]);
                /**
                 * zipoutputstream流和其他的output流不一樣的地方就是BufferedOutputStream不能巢狀它。也就是它不能套快取流用。
                 * 對於資料夾下包含資料夾需要特殊考慮。判斷它是不是資料夾。
                 * 資料夾的話要遍歷他的子節點檔案。用遞迴思想。已在程式碼中給出註釋。還有檔案要注意相對絕對路徑。
                 */
                out.putNextEntry(entry);
                byte[] buffer = new byte[2048];
                // 讀檔案流
                in = new DataInputStream(urlCon.getInputStream());
                int len = 0;
                while ((len = in.read(buffer)) > 0) {
                    out.write(buffer, 0, len);
                    out.flush();
                }
                out.closeEntry();
                in.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                /**out流在最外層關閉,如果迴圈的話提前關閉會直接報錯*/
                if (null != in) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
}

/**
 * [強制]在實現的HostnameVerifier子類中,
 * 需要使用verify函式效驗伺服器主機名的合法性,否則會導致惡意程式利用中間人攻擊繞過主機名效驗。
 * 說明:
 * 在握手期間,如果URL的主機名和伺服器的標識主機名不匹配,
 * 則驗證機制可以回撥此介面實現程式來確定是否應該允許此連線,
 * 如果回撥內實現不恰當,預設接受所有域名,則有安全風險
 */
class myHostnameVerifier implements HostnameVerifier {
    @Override
    public boolean verify(String s, SSLSession sslSession) {
        return true;
    }
}