遞歸的使用不當 導致 壓縮文件不能壓縮二級目錄

錯誤展示:

錯誤代碼:


import java.io.*;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * @author 何
 * @date 2019-08-13 15:06
 */
public class MyZipUtil {


    public static void main(String[] args) throws IOException {


        testZip("D:\\zip\\", "D:\\zip\\test.zip", 0);

    }



    /**

     *
     * @param srcFilesAbsoluteParentPath 需要被壓縮的文件的絕對父路徑 例:   D:\\zip   壓縮這個目錄下所有的文件
     * @param targetZipFileAbsolutePath  需要壓縮到指定的位置的絕對路徑(含文件名) 例:  D:\\zip\\test.zip  壓縮文件就放在這個位置
     * @throws IOException
     */
    public static void testZip(String srcFilesAbsoluteParentPath, String targetZipFileAbsolutePath, int depth)throws IOException{

        //列出此目錄所有文件,遍歷操作
        File parentPath = new File(srcFilesAbsoluteParentPath);
        File[] files = parentPath.listFiles();

        //創建一個Zip文件輸出流
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(targetZipFileAbsolutePath));

        for (int i = 0; i < files.length; i++) {
            File currentFile = files[i];

            //如果是目錄則遞歸調用此方法
            if(currentFile.isDirectory()){
                testZip(currentFile.getAbsolutePath()+"\\", targetZipFileAbsolutePath, depth+1);
                continue;
            }



            /*
                根據目錄深度,拼接zip文件裏面的各個深度的文件的層次結構
                  比如 在外面相對路徑是 dir1/dir2/test.txt ,這個時候的文件名test.txt是不可以直接拿來作爲entry的名字的,
                  應該根據深度,獲取他們的父目錄的名稱 拼接爲  dir1/dir2/test.txt這樣子才能保證層次結構不亂
             */
            StringBuilder entryName = new StringBuilder(files[i].getName());

            File tempFile = files[i];
            for (int j = 0; j < depth; j++) {
                tempFile = tempFile.getParentFile();
                entryName.insert(0, tempFile.getName() + "\\");
            }

            zos.putNextEntry(new ZipEntry(entryName.toString()));


            //根據此文件創建輸入流,用於等下文件的複製
            FileInputStream fis = new FileInputStream(currentFile.getAbsolutePath());


            //文件複製
            byte[] bytes = new byte[1024];
            int readedInt = 0;
            while( (readedInt=fis.read(bytes, 0, bytes.length))!=-1){
                zos.write(bytes, 0, readedInt);
            }
            fis.close();  //流要關閉

        }

        zos.finish();
        zos.close();
    }


}

執行前文件結構截圖

執行後的壓縮文件內部截圖

發現壓縮文件的時候,並沒有把更深層次的文件壓縮進去。

 

錯誤原因:

如圖可以看到,每次遞歸調用自身的時候,輸出流的引用都指向了的更新的對象。即“對象”被“重置”了。

大概文件夾目錄的文件雖然被正常壓縮進去了,可是被外圍的遞歸方法中產生的新的輸出流給 覆蓋 了。所以這就是爲什麼沒報錯,卻也沒有將子目錄裏面的文件壓縮進去的原因。(本來壓縮了,由於遞歸方法內我將輸出流重置的原因,他被沖洗掉了)

警惕:

慎用遞歸,在循環調用自身的時候,一定要想清楚,哪些是在變的,哪些是不在變的。像我這個例子就是沒有注意到 這個壓縮文件應該自始至終都是“一個輸出流”纔對

 

 

貼上改正後自己寫的Zip格式非加密 壓縮與解壓縮的工具類

ZipUtils.java

package com.zmedu.sdk.utils;

import org.junit.Test;

import java.io.*;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * @author 何
 * @date 2019-08-13 15:06
 */
public class MyZipUtil {


    public static void main(String[] args) throws IOException {


//        testZip("D:\\zip\\", "D:\\zip\\test.zip", 0);
        testUnzip("D:\\students\\students.zip", "D:\\students\\");

    }



    /**

     *
     * @param srcFilesAbsoluteParentPath 需要被壓縮的文件的絕對父路徑 例:   D:\\zip   壓縮這個目錄下所有的文件
     * @param targetZipFileAbsolutePath  需要壓縮到指定的位置的絕對路徑(含文件名) 例:  D:\\zip\\test.zip  壓縮文件就放在這個位置
     * @throws IOException
     */
    public static void testZip(String srcFilesAbsoluteParentPath, String targetZipFileAbsolutePath, int depth)throws IOException{

        //列出此目錄所有文件,遍歷操作
        File parentPath = new File(srcFilesAbsoluteParentPath);
        File[] files = parentPath.listFiles();

        //創建一個Zip文件輸出流
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(targetZipFileAbsolutePath));

        for (int i = 0; i < files.length; i++) {
            File currentFile = files[i];

            //如果是目錄則遞歸調用此方法
            if(currentFile.isDirectory()){
                testZip(currentFile.getAbsolutePath()+"\\", targetZipFileAbsolutePath, depth+1);
                continue;
            }



            /*
                根據目錄深度,拼接zip文件裏面的各個深度的文件的層次結構
                  比如 在外面相對路徑是 dir1/dir2/test.txt ,這個時候的文件名test.txt是不可以直接拿來作爲entry的名字的,
                  應該根據深度,獲取他們的父目錄的名稱 拼接爲  dir1/dir2/test.txt這樣子才能保證層次結構不亂
             */
            StringBuilder entryName = new StringBuilder(files[i].getName());

            File tempFile = files[i];
            for (int j = 0; j < depth; j++) {
                tempFile = tempFile.getParentFile();
                entryName.insert(0, tempFile.getName() + "\\");
            }

            zos.putNextEntry(new ZipEntry(entryName.toString()));



            //根據此文件創建輸入流,用於等下文件的複製
            FileInputStream fis = new FileInputStream(currentFile.getAbsolutePath());




            //文件複製
            byte[] bytes = new byte[1024];
            int readedInt = 0;
            while( (readedInt=fis.read(bytes, 0, bytes.length))!=-1){
                zos.write(bytes, 0, readedInt);
            }
            fis.close();  //流要關閉

        }

        zos.finish();
        zos.close();
    }






    /**
     * 解壓Zip格式的非加密壓縮文件
     *  提示:可以解壓子目錄的文件到對應的結構下,即這個壓縮文件的子目錄下還有孫子目錄,也能一併解壓
     *  缺點:不能解壓裏面的zip文件
     *  思路:可將每個 .zip 後綴的文件作爲一個ZipFile的實例對象,調用.size()方法,拋出
     *                  java.util.zip.ZipException: error in opening zip file
     *       上面異常的則表示這個是不能打開的壓縮文件,或者壓根就不是壓縮文件
     * @param zipFileAbsolutePath 壓縮文件的絕對路徑
     * @param destinationAbusolutePath 解壓目的地的絕對路徑
     * @throws IOException
     */
    public static void testUnzip(String zipFileAbsolutePath, String destinationAbusolutePath) throws IOException {

        //根據壓縮文件創建一個文件輸入流,再創建一個Zip的專門的輸入流
        FileInputStream fis = new FileInputStream(zipFileAbsolutePath); //"D:\\test\\test.zip"
        ZipInputStream zis = new ZipInputStream(fis);


        //可以當成壓縮文件裏面的條目
        ZipEntry zipEntry = null;

        //遍歷所有條目 ,本質上就是把他們全部給複製一份出來
        while((zipEntry = zis.getNextEntry())!=null){
            System.out.println("當前文件: " + zipEntry.getName() + ", 大小: " + zipEntry.getSize());
//            ZipFile zipFile = new ZipFile("D:\\unzip\\FacePictures\\xxx\\Faces List.txt");
//            System.out.println(zipFile.size());


            //如果是目錄則建個目錄,不用自己去深度遍歷,zis.getNextEntry會自動獲取本壓縮文件下的所有文件
            if(zipEntry.isDirectory()){
                File file = new File(destinationAbusolutePath+zipEntry.getName());
                file.mkdirs();
                continue;

            }

            //創建一個輸出流,文件輸出流或者字節數組輸出流都行,二選一即可
            File beCreateedFile = new File(destinationAbusolutePath+zipEntry.getName());

            System.out.println("beCreateedFile: " + beCreateedFile);

            if (!(beCreateedFile.getParentFile().exists())) {

                beCreateedFile.getParentFile().mkdirs();
            }


            FileOutputStream fos = new FileOutputStream(beCreateedFile); //"D:\\test\\"


            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();



            /*  第一種: 使用字節數組輸出流的方式   */
            /*****************************************/
            byte[] bytes = new byte[1024];  //長度隨意

            int readedInt = 0;
            while((readedInt = zis.read(bytes, 0, bytes.length)) != -1){
                fos.write(bytes, 0, readedInt);
                byteArrayOutputStream.write(bytes, 0, readedInt);
            }
            fos.close();
            /*****************************************/



            /*  第二種:  使用字節數組輸出流的方式   */
            /*****************************************/
//            byte[] imgBytes = byteArrayOutputStream.toByteArray();
//            fos = new FileOutputStream(destinationAbusolutePath+zipEntry.getName());
//            fos.write(imgBytes);
//            fos.close();
            /*****************************************/


            zis.closeEntry(); // 文檔說要關閉,可是不關閉也可以,還是關閉把
        }



        //流要記得關閉!
        zis.close();
        fis.close();


    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章