Dosyaları Android'de programlı olarak nasıl açarım?


131

Belirli bir .zip dosyasından birkaç dosyayı açan ve sıkıştırılmış dosyadaki biçime göre ayrı dosyalar veren küçük bir kod parçacığına ihtiyacım var. Lütfen bilginizi gönderin ve bana yardım edin.


1
Kotlin çözümünü buradan edinebilirsiniz - stackoverflow.com/a/50990992/1162784
arsent

Yanıtlar:


140

Peno'nun sürümü biraz optimize edildi. Performanstaki artış algılanabilir.

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

1
Bence evet, işe yarıyor, çünkü bir şeyleri paketlemenin çok olağan bir yolu. Doğru 'yolu' ve 'posta adını' almayı başarın. Ayrıca ilginizi çekebilecek bazı şeyler de gördüm (zaten görmüşsünüzdür): link
Vasily Sochinsky

1
Çünkü, eğer zebir dizin iseniz "sadece dosya" işlemlerini atlamanız gerekir . Bu işlemleri gerçekleştirmeye çalışmak bir istisnaya neden olacaktır.
Vasily Sochinsky

1
Bu cevap işe yaramaz, çünkü üzerine veri yazmak için eksik dosyaları oluşturmaz !!
Omar HossamEldin

1
Aslında, zip dosyası gereksiz yol olmadan oluşturulursa bu kod çalışmaz, örneğin, bir APK dosyasını açmak için bu kodu çalıştırabilirsiniz, FileNotFoundException alırsınız.
Shaw

99

Vasily Sochinsky'nin cevabına göre biraz değiştirilmiş ve küçük bir düzeltme ile:

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

Önemli farklılıklar

  • public static - Bu, herhangi bir yerde bulunabilen statik bir yardımcı program yöntemidir.
  • 2 Fileparametre çünkü String: / dosyalar için ve biri zip dosyasının daha önce nerede çıkarılacağını belirtemedi. Ayrıca path + filenamebirleştirme> https://stackoverflow.com/a/412495/995891
  • throws- çünkü geç yakalamak - gerçekten ilgilenmiyorsan bir dene yakala ekle.
  • aslında gerekli dizinlerin her durumda mevcut olduğundan emin olur. Her zip dosyası, dosya girişlerinden önce gerekli tüm dizin girdilerini içermez. Bunda 2 potansiyel hata vardı:
    • zip boş bir dizin içeriyorsa ve ortaya çıkan dizin yerine mevcut bir dosya varsa, bu göz ardı edildi. Dönüş değeri mkdirs()önemlidir.
    • dizin içermeyen zip dosyalarında çökebilir.
  • artan yazma arabelleği boyutu, bu performansı biraz artırmalıdır. Depolama genellikle 4k bloklar halindedir ve daha küçük parçalar halinde yazma genellikle gerekenden daha yavaştır.
  • finallykaynak sızıntılarını önlemek için büyüsünü kullanır .

Yani

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

orijinalinin eşdeğerini yapmalı

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

merhaba, sdcard / temp / 768 \ 769.json gibi geriye doğru eğik çizgi ile yol alıyorum, bu yüzden hata alıyorum, bunu nasıl yöneteceğimi söyleyebilir misin
Ando Masahashi

@AndoMasahashi, linux dosya sisteminde yasal bir dosya adı olmalıdır. Hangi hatayı alıyorsunuz ve dosya adı sonunda nasıl görünmeli?
zapl

/sdcard/pictures\picturess.jpeg gibi görünüyor ve hata dosyası bulunamadı hatası
Ando Masahashi

İyi çalışıyor, ancak zip içindeki dosya adlarından biri içinde olmadığında istisna atıyor UTF8 format. Bunun yerine apache'nin lib'sini kullanan bu kodu kullandım commons-compress.
Ashish Tanna

@AshishTanna gerçekten, bilinen bir Sorun blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in
zapl

26

Bu, kullandığım unzip yöntemim:

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

genişletme APK Genişletme Dosyaları obb dosyalarını açmak veya açmak için aynı kodun çalıştığını düşünüyor musunuz?
LOG_TAG


10

Kotlin yolu

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

}

kullanım

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

7

Zaten burada olan cevaplar işe yarasa da, umduğumdan biraz daha yavaş olduklarını gördüm. Bunun yerine , hızı nedeniyle en iyi çözüm olduğunu düşündüğüm zip4j kullandım . Ayrıca, yararlı bulduğum sıkıştırma miktarı için farklı seçeneklere de izin verdi.


6

UPDATE 2016 aşağıdaki sınıfı kullanın

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


 }

Nasıl kullanılır

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

İzinler

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

dosya adını görebilir, ancak dosyayı dışa aktarmaya çalışırken FileNotFoundException hatası alıyorum
Parth Anjaria

5

@Zapl cevabına göre, ilerleme raporu ile Unzip:

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

 }

-> Yardımcı Sınıf (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 düzeni (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 dosyasında izin:

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

2

İşte bir ZipFileIterator (java Yineleyici gibi, ancak zip dosyaları için):

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

genişletme APK Genişletme Dosyaları obb dosyalarını açmak veya açmak için aynı kodun çalıştığını düşünüyor musunuz?
LOG_TAG

2

Minimal örnek, zip dosyamdan belirli bir dosyayı uygulama önbellek klasörüme açardım. Daha sonra manifest dosyasını farklı bir yöntem kullanarak okudum.

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'nın ZipFile sınıfının işleyemediği zip dosyalarıyla çalışıyorum. Görünüşe göre Java 8 sıkıştırma yöntemi 12'yi işleyemiyor (bzip2'ye inanıyorum). Zip4j dahil bir dizi yöntemi denedikten sonra (bu, başka bir sorundan dolayı bu belirli dosyalarda da başarısız olur), Apache'nin burada belirtildiği gibi ek sıkıştırma yöntemlerini destekleyen ortak sıkıştırma ile başarılı oldum .

Aşağıdaki ZipFile sınıfının java.util.zip'deki sınıf olmadığını unutmayın.

Aslında org.apache.commons.compress.archivers.zip.ZipFile olduğundan içe aktarmalara dikkat edin.

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 için:

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

2

Zapl'ın cevabına göre, try()etrafına eklemek, Closeablekullanımdan sonra akışları otomatik olarak kapatır.

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

Stream.CopyTo'dan Jon Skeet'in buradaki cevabından alınan Constant.DefaultBufferSize( 65536) kullanarak : https://stackoverflow.com/a/411605/1876355C# .NET 4

Her zaman sadece byte[1024]veya kullanarak gönderiler görüyorumbyte[4096] tampon , daha büyük olabileceğini, performansı artıran ve hala mükemmel şekilde normal çalıştığını bilmiyordum.

İşte StreamKaynak kodu: 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;

Ancak, ben geri onu çevrilen 65536hangi da bir katıdır 4096Emin olmak için.


1
Bu, bu konudaki en iyi çözümdür. Ek olarak, FileOutputStream ile birlikte BufferedOutputStream'i de kullanırdım.
MarkoR

1

Şifre Korumalı Zip Dosyası

Şifreli dosyaları sıkıştırmak istiyorsanız, şifreli dosyaları kolayca sıkıştırabilen bu kitaplığa göz atabilirsiniz:

zip:

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

Sıkıştırılmış:

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

rar:

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

Bu kütüphanenin dokümantasyonu yeterince iyi, oradan birkaç örnek ekledim. Tamamen ücretsizdir ve android için özel olarak yazılmıştır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.