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


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.