안드로이드에서 프로그래밍 방식으로 파일을 압축 해제하는 방법은 무엇입니까?


131

주어진 .zip 파일에서 몇 개의 파일을 압축 해제하고 압축 파일의 형식에 따라 별도의 파일을 제공하는 작은 코드 스 니펫이 필요합니다. 당신의 지식을 게시하고 도와주세요.


1
당신은 여기에 코 틀린 솔루션을 얻을 수 있습니다 - stackoverflow.com/a/50990992/1162784
— arsent

답변:


140

peno의 버전이 약간 최적화되었습니다. 성능이 향상됩니다.

private boolean unpackZip(String path, String zipname)
{       
     InputStream is;
     ZipInputStream zis;
     try 
     {
         String filename;
         is = new FileInputStream(path + zipname);
         zis = new ZipInputStream(new BufferedInputStream(is));          
         ZipEntry ze;
         byte[] buffer = new byte[1024];
         int count;

         while ((ze = zis.getNextEntry()) != null) 
         {
             filename = ze.getName();

             // Need to create directories if not exists, or
             // it will generate an Exception...
             if (ze.isDirectory()) {
                File fmd = new File(path + filename);
                fmd.mkdirs();
                continue;
             }

             FileOutputStream fout = new FileOutputStream(path + filename);

             while ((count = zis.read(buffer)) != -1) 
             {
                 fout.write(buffer, 0, count);             
             }

             fout.close();               
             zis.closeEntry();
         }

         zis.close();
     } 
     catch(IOException e)
     {
         e.printStackTrace();
         return false;
     }

    return true;
}

12
<uses-permission android : name = "android.permission.WRITE_EXTERNAL_STORAGE"/>
— Lou Morda

1
나는 그렇다고 생각합니다. 포장을 풀 때 매우 일반적인 방법이기 때문입니다. 올바른 '경로'와 'zipname'을 얻으십시오. 나는 또한 당신이 관심을 가질만한 것들을 보았다. (당신은 이미 그것을 보았 음) : link
— Vasily Sochinsky

1
ze디렉토리 인 경우 "파일 전용"조작을 건너 뛰어야하므로 . 이러한 작업을 수행하려고하면 예외가 발생합니다.
— Vasily Sochinsky

1
이 답변은 데이터를 쓰기 위해 누락 된 파일을 만들지 않기 때문에 작동하지 않아야합니다 !!
— Omar HossamEldin

1
실제로 zip 파일이 정크 경로없이 생성되면이 코드가 작동하지 않습니다. 예를 들어,이 코드를 실행하여 APK 파일의 압축을 풀면 FileNotFoundException이 발생합니다.
— Shaw

99

Vasily Sochinsky의 답변을 약간 수정하고 약간 수정했습니다.

public static void unzip(File zipFile, File targetDirectory) throws IOException {
    ZipInputStream zis = new ZipInputStream(
            new BufferedInputStream(new FileInputStream(zipFile)));
    try {
        ZipEntry ze;
        int count;
        byte[] buffer = new byte[8192];
        while ((ze = zis.getNextEntry()) != null) {
            File file = new File(targetDirectory, ze.getName());
            File dir = ze.isDirectory() ? file : file.getParentFile();
            if (!dir.isDirectory() && !dir.mkdirs())
                throw new FileNotFoundException("Failed to ensure directory: " +
                        dir.getAbsolutePath());
            if (ze.isDirectory())
                continue;
            FileOutputStream fout = new FileOutputStream(file);
            try {
                while ((count = zis.read(buffer)) != -1)
                    fout.write(buffer, 0, count);
            } finally {
                fout.close();
            }
            /* if time should be restored as well
            long time = ze.getTime();
            if (time > 0)
                file.setLastModified(time);
            */
        }
    } finally {
        zis.close();
    }
}

주목할만한 차이점

  • public static -이것은 어디에나있을 수있는 정적 유틸리티 방법입니다.
  • 이 File때문에 매개 변수 String파일 /와 zip 파일이 이전에 추출 될 위치를 하나 지정하지 수 : 있습니다. 또한 path + filename연결> https://stackoverflow.com/a/412495/995891
  • throws- 늦게 잡기 때문에 -관심이 없다면 시도 잡기를 추가하십시오.
  • 실제로 필요한 디렉토리가 모든 경우에 존재하는지 확인합니다. 모든 zip에 파일 항목 이전에 필요한 모든 디렉토리 항목이있는 것은 아닙니다. 여기에는 2 가지 잠재적 버그가있었습니다.
    • zip에 빈 디렉토리가 있고 결과 디렉토리 대신 기존 파일이 있으면 무시됩니다. 의 반환 값 mkdirs()이 중요합니다.
    • 디렉토리가없는 zip 파일에서 충돌이 발생할 수 있습니다.
  • 쓰기 버퍼 크기가 증가하면 성능이 약간 향상됩니다. 스토리지는 일반적으로 4k 블록으로 이루어지며 작은 청크로 쓰기는 보통 필요한 것보다 느립니다.
  • 의 finally리소스를 사용하여 리소스 누수를 방지합니다.

그래서

unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));

원본과 동등한 작업을 수행해야합니다

unpackZip("/sdcard/", "pictures.zip")

안녕하세요 메신저 sdcard / temp / 768 \ 769.json 같은 역 슬래시 경로를 받고 그래서 내가 오류를 받고 그것을 관리하는 방법을 말해 줄 수 있습니다
— Ando Masahashi

@AndoMasahashi는 Linux 파일 시스템에서 유효한 파일 이름이어야합니다. 어떤 오류가 발생하며 파일 이름이 끝에 어떻게 표시됩니까?
— zapl

그것은 /sdcard/pictures\picturess.jpeg처럼 보이고 오류 파일을 찾을 수 없습니다 오류
— Ando Masahashi

잘 작동하지만 zip 내의 파일 이름 중 하나가에 없으면 예외가 발생 UTF8 format합니다. 그래서 아파치의 lib 를 사용하는 대신 이 코드 를 사용했습니다 commons-compress.
— Ashish Tanna

@AshishTanna 실제로, 그것은 알려진 문제 blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in
— zapl

26

이것은 내가 사용하는 압축 해제 방법입니다.

private boolean unpackZip(String path, String zipname)
{       
     InputStream is;
     ZipInputStream zis;
     try 
     {
         is = new FileInputStream(path + zipname);
         zis = new ZipInputStream(new BufferedInputStream(is));          
         ZipEntry ze;

         while((ze = zis.getNextEntry()) != null) 
         {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             byte[] buffer = new byte[1024];
             int count;

             String filename = ze.getName();
             FileOutputStream fout = new FileOutputStream(path + filename);

             // reading and writing
             while((count = zis.read(buffer)) != -1) 
             {
                 baos.write(buffer, 0, count);
                 byte[] bytes = baos.toByteArray();
                 fout.write(bytes);             
                 baos.reset();
             }

             fout.close();               
             zis.closeEntry();
         }

         zis.close();
     } 
     catch(IOException e)
     {
         e.printStackTrace();
         return false;
     }

    return true;
}

확장 APK 확장 파일 obb 파일의 압축을 풀거나 풀 때 동일한 코드가 작동한다고 생각하십니까?
— LOG_TAG


10

코 틀린 방식

//FileExt.kt

data class ZipIO (val entry: ZipEntry, val output: File)

fun File.unzip(unzipLocationRoot: File? = null) {

    val rootFolder = unzipLocationRoot ?: File(parentFile.absolutePath + File.separator + nameWithoutExtension)
    if (!rootFolder.exists()) {
       rootFolder.mkdirs()
    }

    ZipFile(this).use { zip ->
        zip
        .entries()
        .asSequence()
        .map {
            val outputFile = File(rootFolder.absolutePath + File.separator + it.name)
            ZipIO(it, outputFile)
        }
        .map {
            it.output.parentFile?.run{
                if (!exists()) mkdirs()
            }
            it
        }
        .filter { !it.entry.isDirectory }
        .forEach { (entry, output) ->
            zip.getInputStream(entry).use { input ->
                output.outputStream().use { output ->
                    input.copyTo(output)
                }
            }
        }
    }

}

용법

val zipFile = File("path_to_your_zip_file")
file.unzip()

7

이미 여기에있는 답변이 잘 작동하는 동안 나는 그들이 기대했던 것보다 약간 느리다는 것을 알았습니다. 대신 zip4j을 사용 했는데 속도 때문에 최고의 솔루션이라고 생각합니다. 또한 압축 량에 대해 다른 옵션을 사용할 수 있었으므로 유용하다고 생각했습니다.


6

UPDATE 2016은 다음 클래스를 사용합니다

    package com.example.zip;

    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipInputStream;
    import android.util.Log;

    public class DecompressFast {



 private String _zipFile; 
  private String _location; 

  public DecompressFast(String zipFile, String location) { 
    _zipFile = zipFile; 
    _location = location; 

    _dirChecker(""); 
  } 

  public void unzip() { 
    try  { 
      FileInputStream fin = new FileInputStream(_zipFile); 
      ZipInputStream zin = new ZipInputStream(fin); 
      ZipEntry ze = null; 
      while ((ze = zin.getNextEntry()) != null) { 
        Log.v("Decompress", "Unzipping " + ze.getName()); 

        if(ze.isDirectory()) { 
          _dirChecker(ze.getName()); 
        } else { 
          FileOutputStream fout = new FileOutputStream(_location + ze.getName()); 
         BufferedOutputStream bufout = new BufferedOutputStream(fout);
          byte[] buffer = new byte[1024];
          int read = 0;
          while ((read = zin.read(buffer)) != -1) {
              bufout.write(buffer, 0, read);
          }




          bufout.close();

          zin.closeEntry(); 
          fout.close(); 
        } 

      } 
      zin.close(); 


      Log.d("Unzip", "Unzipping complete. path :  " +_location );
    } catch(Exception e) { 
      Log.e("Decompress", "unzip", e); 

      Log.d("Unzip", "Unzipping failed");
    } 

  } 

  private void _dirChecker(String dir) { 
    File f = new File(_location + dir); 

    if(!f.isDirectory()) { 
      f.mkdirs(); 
    } 
  } 


 }

사용하는 방법

 String zipFile = Environment.getExternalStorageDirectory() + "/the_raven.zip"; //your zip file location
    String unzipLocation = Environment.getExternalStorageDirectory() + "/unzippedtestNew/"; // destination folder location
  DecompressFast df= new DecompressFast(zipFile, unzipLocation);
    df.unzip();

권한

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

파일 이름을 볼 수 있지만 파일을 초과하려고 할 때 FileNotFoundException 오류가 발생합니다
— Parth Anjaria

5

@zapl 답변에 따르면 진행 보고서와 함께 압축을 풉니 다.

public interface UnzipFile_Progress
{
    void Progress(int percent, String FileName);
}

// unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));
public static void UnzipFile(File zipFile, File targetDirectory, UnzipFile_Progress progress) throws IOException,
        FileNotFoundException
{
    long total_len = zipFile.length();
    long total_installed_len = 0;

    ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
    try
    {
        ZipEntry ze;
        int count;
        byte[] buffer = new byte[1024];
        while ((ze = zis.getNextEntry()) != null)
        {
            if (progress != null)
            {
                total_installed_len += ze.getCompressedSize();
                String file_name = ze.getName();
                int percent = (int)(total_installed_len * 100 / total_len);
                progress.Progress(percent, file_name);
            }

            File file = new File(targetDirectory, ze.getName());
            File dir = ze.isDirectory() ? file : file.getParentFile();
            if (!dir.isDirectory() && !dir.mkdirs())
                throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
            if (ze.isDirectory())
                continue;
            FileOutputStream fout = new FileOutputStream(file);
            try
            {
                while ((count = zis.read(buffer)) != -1)
                    fout.write(buffer, 0, count);
            } finally
            {
                fout.close();
            }

            // if time should be restored as well
            long time = ze.getTime();
            if (time > 0)
                file.setLastModified(time);
        }
    } finally
    {
        zis.close();
    }
}

3
public class MainActivity extends Activity {

private String LOG_TAG = MainActivity.class.getSimpleName();

private File zipFile;
private File destination;

private TextView status;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    status = (TextView) findViewById(R.id.main_status);
    status.setGravity(Gravity.CENTER);

    if ( initialize() ) {
        zipFile = new File(destination, "BlueBoxnew.zip");
        try {
            Unzipper.unzip(zipFile, destination);
            status.setText("Extracted to \n"+destination.getAbsolutePath());
        } catch (ZipException e) {
            Log.e(LOG_TAG, e.getMessage());
        } catch (IOException e) {
            Log.e(LOG_TAG, e.getMessage());
        }
    } else {
        status.setText("Unable to initialize sd card.");
    }
}

public boolean initialize() {
    boolean result = false;
     File sdCard = new File(Environment.getExternalStorageDirectory()+"/zip/");
    //File sdCard = Environment.getExternalStorageDirectory();
    if ( sdCard != null ) {
        destination = sdCard;
        if ( !destination.exists() ) {
            if ( destination.mkdir() ) {
                result = true;
            }
        } else {
            result = true;
        }
    }

    return result;
}

 }

-> 헬퍼 클래스 (Unzipper.java)

    import java.io.File;
    import java.io.FileInputStream;
   import java.io.FileOutputStream;
    import java.io.IOException;
       import java.util.zip.ZipEntry;
    import java.util.zip.ZipException;
    import java.util.zip.ZipInputStream;
     import android.util.Log;

   public class Unzipper {

private static String LOG_TAG = Unzipper.class.getSimpleName();

public static void unzip(final File file, final File destination) throws ZipException, IOException {
    new Thread() {
        public void run() {
            long START_TIME = System.currentTimeMillis();
            long FINISH_TIME = 0;
            long ELAPSED_TIME = 0;
            try {
                ZipInputStream zin = new ZipInputStream(new FileInputStream(file));
                String workingDir = destination.getAbsolutePath()+"/";

                byte buffer[] = new byte[4096];
                int bytesRead;
                ZipEntry entry = null;
                while ((entry = zin.getNextEntry()) != null) {
                    if (entry.isDirectory()) {
                        File dir = new File(workingDir, entry.getName());
                        if (!dir.exists()) {
                            dir.mkdir();
                        }
                        Log.i(LOG_TAG, "[DIR] "+entry.getName());
                    } else {
                        FileOutputStream fos = new FileOutputStream(workingDir + entry.getName());
                        while ((bytesRead = zin.read(buffer)) != -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                        fos.close();
                        Log.i(LOG_TAG, "[FILE] "+entry.getName());
                    }
                }
                zin.close();

                FINISH_TIME = System.currentTimeMillis();
                ELAPSED_TIME = FINISH_TIME - START_TIME;
                Log.i(LOG_TAG, "COMPLETED in "+(ELAPSED_TIME/1000)+" seconds.");
            } catch (Exception e) {
                Log.e(LOG_TAG, "FAILED");
            }
        };
    }.start();
}

   }

-> xml 레이아웃 (activity_main.xml) :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context=".MainActivity" >

<TextView
    android:id="@+id/main_status"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:text="@string/hello_world" />

</RelativeLayout>

-> Menifest 파일의 권한 :

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2

다음은 ZipFileIterator입니다 (Java Iterator와 유사하지만 zip 파일 용).

package ch.epfl.bbp.io;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ZipFileIterator implements Iterator<File> {

    private byte[] buffer = new byte[1024];

    private FileInputStream is;
    private ZipInputStream zis;
    private ZipEntry ze;

    public ZipFileIterator(File file) throws FileNotFoundException {
    is = new FileInputStream(file);
    zis = new ZipInputStream(new BufferedInputStream(is));
    }

    @Override
    public boolean hasNext() {
    try {
        return (ze = zis.getNextEntry()) != null;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
    }

    @Override
    public File next() {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int count;

        String filename = ze.getName();
        File tmpFile = File.createTempFile(filename, "tmp");
        tmpFile.deleteOnExit();// TODO make it configurable
        FileOutputStream fout = new FileOutputStream(tmpFile);

        while ((count = zis.read(buffer)) != -1) {
        baos.write(buffer, 0, count);
        byte[] bytes = baos.toByteArray();
        fout.write(bytes);
        baos.reset();
        }
        fout.close();
        zis.closeEntry();

        return tmpFile;

    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    }

    @Override
    public void remove() {
    throw new RuntimeException("not implemented");
    }

    public void close() {
    try {
        zis.close();
        is.close();
    } catch (IOException e) {// nope
    }
    }
}

확장 APK 확장 파일 obb 파일의 압축을 풀거나 풀 때 동일한 코드가 작동한다고 생각하십니까?
— LOG_TAG

2

최소한의 예는 zip 파일에서 특정 파일을 응용 프로그램 캐시 폴더로 압축 해제하는 데 사용되었습니다. 그런 다음 다른 방법을 사용하여 매니페스트 파일을 읽습니다.

private void unzipUpdateToCache() {
    ZipInputStream zipIs = new ZipInputStream(context.getResources().openRawResource(R.raw.update));
    ZipEntry ze = null;

    try {

        while ((ze = zipIs.getNextEntry()) != null) {
            if (ze.getName().equals("update/manifest.json")) {
                FileOutputStream fout = new FileOutputStream(context.getCacheDir().getAbsolutePath() + "/manifest.json");

                byte[] buffer = new byte[1024];
                int length = 0;

                while ((length = zipIs.read(buffer))>0) {
                    fout.write(buffer, 0, length);
                }
                zipIs .closeEntry();
                fout.close();
            }
        }
        zipIs .close();

    } catch (IOException e) {
        e.printStackTrace();
    }

}

2

Java의 ZipFile 클래스가 처리 할 수없는 zip 파일로 작업하고 있습니다. Java 8은 분명히 압축 방법 12를 처리 할 수 ​​없습니다 (bzip2라고 생각합니다). zip4j (다른 문제로 인해 이러한 특정 파일에서도 실패 함)를 포함한 여러 가지 방법을 시도한 후 여기에 언급 된 추가 압축 방법 을 지원하는 Apache의 commons-compress 를 성공적으로 수행 했습니다 .

아래 ZipFile 클래스는 java.util.zip의 클래스가 아닙니다.

실제로는 org.apache.commons.compress.archivers.zip.ZipFile 이므로 가져 오기에주의하십시오.

try (ZipFile zipFile = new ZipFile(archiveFile)) {
    Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
    while (entries.hasMoreElements()) {
        ZipArchiveEntry entry = entries.nextElement();
        File entryDestination = new File(destination, entry.getName());
        if (entry.isDirectory()) {
            entryDestination.mkdirs();
        } else {
            entryDestination.getParentFile().mkdirs();
            try (InputStream in = zipFile.getInputStream(entry); OutputStream out = new FileOutputStream(entryDestination)) {
                IOUtils.copy(in, out);
            }
        }
    }
} catch (IOException ex) {
    log.debug("Error unzipping archive file: " + archiveFile, ex);
}

Gradle의 경우 :

compile 'org.apache.commons:commons-compress:1.18'

2

zapl의 답변에 따라 try()주위를 추가하면 Closeable사용 후 스트림이 자동으로 닫힙니다.

public static void unzip(File zipFile, File targetDirectory) {
    try (FileInputStream fis = new FileInputStream(zipFile)) {
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            try (ZipInputStream zis = new ZipInputStream(bis)) {
                ZipEntry ze;
                int count;
                byte[] buffer = new byte[Constant.DefaultBufferSize];
                while ((ze = zis.getNextEntry()) != null) {
                    File file = new File(targetDirectory, ze.getName());
                    File dir = ze.isDirectory() ? file : file.getParentFile();
                    if (!dir.isDirectory() && !dir.mkdirs())
                        throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
                    if (ze.isDirectory())
                        continue;
                    try (FileOutputStream fout = new FileOutputStream(file)) {
                        while ((count = zis.read(buffer)) != -1)
                            fout.write(buffer, 0, count);
                    }
                }
            }
        }
    } catch (Exception ex) {
        //handle exception
    }
}

Jon Skeet의 답변 에서 Stream.CopyTo 에서 얻은 Constant.DefaultBufferSize( 65536) 사용 : https://stackoverflow.com/a/411605/1876355C# .NET 4

나는 항상 그냥 사용 byte[1024]하거나 byte[4096]버퍼를 사용하여 게시물을보고 , 그것이 더 커질 수 있다는 것을 결코 알지 못하여 성능을 향상시키고 여전히 정상적으로 작동합니다.

Stream소스 코드 는 다음과 같습니다 . https://referencesource.microsoft.com/#mscorlib/system/io/stream.cs

//We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
// improvement in Copy performance.

private const int _DefaultCopyBufferSize = 81920;

그러나 나는 다시 전화를 걸어 안전을 위해 65536여러 가지 4096를 사용했습니다.


1
이 스레드에서 가장 좋은 솔루션입니다. 또한 FileOutputStream과 함께 스택에서 BufferedOutputStream을 사용합니다.
— MarkoR

1

비밀번호로 보호 된 Zip 파일

비밀번호로 파일을 압축하려면 비밀번호로 파일을 쉽게 압축 할 수 있는 이 라이브러리 를 살펴볼 수 있습니다.

지퍼:

ZipArchive zipArchive = new ZipArchive();
zipArchive.zip(targetPath,destinationPath,password);

압축 해제 :

ZipArchive zipArchive = new ZipArchive();
zipArchive.unzip(targetPath,destinationPath,password);

라스 :

RarArchive rarArchive = new RarArchive();
rarArchive.extractArchive(file archive, file destination);

이 라이브러리의 문서는 충분합니다. 방금 몇 가지 예를 추가했습니다. 완전히 무료이며 Android 용으로 특별히 작성되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.