Làm cách nào để giải nén tập tin theo chương trình trong Android?


131

Tôi cần một đoạn mã nhỏ để giải nén một vài tệp từ một tệp .zip nhất định và cung cấp các tệp riêng biệt theo định dạng mà chúng có trong tệp được nén. Xin vui lòng gửi kiến ​​thức của bạn và giúp tôi ra.


1
Bạn có thể nhận giải pháp Kotlin tại đây - stackoverflow.com/a/50990992/1162784
arsent

Câu trả lời:


140

Có phiên bản peno tối ưu hóa một chút. Sự gia tăng hiệu suất là có thể cảm nhận được.

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
<used-allow android: name = "ERIC.WRITE_EXTERNAL_STORAGE" />
Lou Morda

1
Tôi nghĩ là có, nó hoạt động, bởi vì đó là cách rất thông thường để giải nén mọi thứ. Chỉ cần quản lý để có được 'đường dẫn' và 'zipname' đúng. Tôi cũng đã thấy một số thứ bạn có thể quan tâm (chắc chắn bạn đã thấy nó rồi): link
Vasily Sochinsky

1
Bởi vì bạn cần bỏ qua các hoạt động "chỉ tập tin" nếu bạn zelà một thư mục. Cố gắng thực hiện các hoạt động này sẽ gây ra một ngoại lệ.
Vasily Sochinsky

1
Câu trả lời này không nên hoạt động, vì nó không tạo ra các tệp bị thiếu để ghi dữ liệu trên đó !!
Omar HossamEldin

1
Trên thực tế, mã này sẽ không hoạt động nếu tệp zip được tạo mà không có đường dẫn rác, ví dụ: bạn có thể chạy mã này để giải nén tệp APK, bạn sẽ nhận được FileNotFoundException.
Shaw

99

Dựa trên câu trả lời của Vasily Sochinsky một chút điều chỉnh & với một sửa chữa nhỏ:

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();
    }
}

Sự khác biệt đáng chú ý

  • public static - đây là một phương thức tiện ích tĩnh có thể ở bất cứ đâu.
  • 2 Filetham số vì String: / cho các tệp và một không thể chỉ định vị trí tệp zip được trích xuất trước đó. Cũng path + filenamenối https://stackoverflow.com/a/412495/995891
  • throws - bởi vì bắt muộn - thêm một thử bắt nếu thực sự không quan tâm đến chúng.
  • thực sự đảm bảo rằng các thư mục cần thiết tồn tại trong mọi trường hợp. Không phải mọi zip đều chứa tất cả các mục nhập thư mục cần thiết trước các mục nhập tệp. Điều này có 2 lỗi tiềm ẩn:
    • nếu zip chứa một thư mục trống và thay vì thư mục kết quả có một tệp hiện có, thì thư mục này đã bị bỏ qua. Giá trị trả về mkdirs()là quan trọng.
    • có thể sập trên các tệp zip không chứa thư mục.
  • tăng kích thước bộ đệm ghi, điều này sẽ cải thiện hiệu suất một chút. Lưu trữ thường ở các khối 4k và viết trong các phần nhỏ hơn thường chậm hơn mức cần thiết.
  • sử dụng phép thuật finallyđể ngăn chặn rò rỉ tài nguyên.

Vì thế

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

nên làm tương đương với bản gốc

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

xin chào tôi đang nhận đường dẫn với dấu gạch chéo ngược như sdcard / temp / 768 \ 769.json vì vậy tôi đang gặp lỗi, bạn có thể cho tôi biết cách quản lý nó không
Ando Masahashi

@AndoMasahashi phải là tên tệp hợp pháp trên hệ thống tệp linux. Lỗi gì bạn nhận được và cuối cùng tên tệp trông như thế nào?
zapl

nó trông giống như /sdcard/pictures\picturess.jpeg và không tìm thấy tệp lỗi
Ando Masahashi

Nó hoạt động tốt, nhưng nó ném ngoại lệ khi một trong các tên tệp bên trong zip không có trong UTF8 format. Vì vậy, tôi đã sử dụng mã này thay vì sử dụng commons-compresslib của apache .
Ashish Tanna

@AshishTanna thực sự, đó là một tiếng Vấn đề blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in
zapl

26

Đây là phương pháp giải nén của tôi, mà tôi sử dụng:

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;
}

Bạn có nghĩ rằng cùng một mã hoạt động để giải nén hoặc giải nén tệp mở rộng APK mở rộng tệp obb?
LOGiah


10

Cách thức của Kotlin

//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)
                }
            }
        }
    }

}

Sử dụng

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

7

Trong khi các câu trả lời đã có ở đây hoạt động tốt, tôi thấy rằng chúng chậm hơn một chút so với tôi mong đợi. Thay vào đó tôi đã sử dụng zip4j , mà tôi nghĩ là giải pháp tốt nhất vì tốc độ của nó. Nó cũng cho phép các tùy chọn khác nhau cho số lượng nén mà tôi thấy hữu ích.


6

CẬP NHẬT 2016 sử dụng lớp sau

    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(); 
    } 
  } 


 }

Cách sử dụng

 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();

Quyền

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

có thể thấy tên tệp, nhưng khi cố gắng vượt quá tệp, tôi gặp lỗi FileNotFoundException
Parth Anjaria

5

Theo câu trả lời @zapl, Giải nén với báo cáo tiến độ:

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;
}

 }

-> Lớp người trợ giúp (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();
}

   }

-> bố cục 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>

-> quyền trong tệp Menifest:

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

2

Đây là ZipFileIterator (như java Iterator, nhưng dành cho các tệp 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
    }
    }
}

Bạn có nghĩ rằng cùng một mã hoạt động để giải nén hoặc giải nén tệp mở rộng APK mở rộng tệp obb?
LOGiah

2

Ví dụ tối thiểu tôi đã sử dụng để giải nén một tệp cụ thể từ tệp zipfile vào thư mục bộ đệm ứng dụng của mình. Sau đó tôi đọc tệp kê khai bằng một phương thức khác.

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

Tôi đang làm việc với các tệp zip mà lớp ZipFile của Java không thể xử lý. Java 8 rõ ràng không thể xử lý phương thức nén 12 (bzip2 tôi tin). Sau khi thử một số phương thức bao gồm zip4j (cũng không thành công với các tệp cụ thể này do một vấn đề khác), tôi đã thành công với commons-nén của Apache , hỗ trợ các phương thức nén bổ sung như được đề cập ở đây .

Lưu ý rằng lớp ZipFile bên dưới không phải là lớp từ java.util.zip.

Đó thực sự là org.apache.commons.compress.archivers.zip.ZipFile vì vậy hãy cẩn thận với việc nhập khẩu.

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);
}

Dành cho học sinh lớp:

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

2

Dựa trên câu trả lời của zapl, việc thêm try()xung quanh Closeablesẽ tự động đóng các luồng sau khi sử dụng.

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
    }
}

Sử dụng Constant.DefaultBufferSize( 65536) nhận được từ C# .NET 4 Stream.CopyTo từ câu trả lời của Jon Skeet tại đây: https://stackoverflow.com/a/411605/1876355

Tôi luôn chỉ thấy các bài đăng sử dụng byte[1024]hoặc byte[4096]bộ đệm, không bao giờ biết nó có thể lớn hơn nhiều giúp cải thiện hiệu suất và vẫn hoạt động hoàn toàn bình thường.

Đây là Streammã nguồn: 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;

Tuy nhiên, tôi đã gọi nó trở lại 65536cũng là một bội số 4096để đảm bảo an toàn.


1
Đây là giải pháp tốt nhất trong chủ đề này. Ngoài ra, tôi cũng sẽ sử dụng BufferedOutputStream trong ngăn xếp với FileOutputStream.
MarkoR

1

Mật khẩu được bảo vệ tập tin Zip

Nếu bạn muốn nén các tệp bằng mật khẩu, bạn có thể xem thư viện này có thể nén các tệp bằng mật khẩu một cách dễ dàng:

Zip:

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

Giải nén:

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

Rar:

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

Tài liệu của thư viện này là đủ tốt, tôi chỉ cần thêm một vài ví dụ từ đó. Nó hoàn toàn miễn phí và được viết đặc biệt cho Android.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.