如何在 Android 中以编程方式解压缩文件?
Posted
技术标签:
【中文标题】如何在 Android 中以编程方式解压缩文件?【英文标题】:How to unzip files programmatically in Android? 【发布时间】:2011-03-23 22:01:27 【问题描述】:我需要一个小代码 sn-p 从给定的 .zip 文件中解压缩几个文件,并根据它们在压缩文件中的格式给出单独的文件。请发布您的知识并帮助我。
【问题讨论】:
您可以在此处获取 Kotlin 解决方案 - ***.com/a/50990992/1162784 【参考方案1】:android 有内置的 Java API。查看java.util.zip 包。
ZipInputStream 类是你应该研究的。从 ZipInputStream 中读取 ZipEntry 并将其转储到文件系统/文件夹中。检查similar example to compress into zip文件。
【讨论】:
您应该提供了一个代码示例。你错过了很多积分。【参考方案2】:这是我使用的解压方法:
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 文件吗?【参考方案3】:这是一个 ZipFileIterator(类似于 java 迭代器,但用于 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 文件吗?【参考方案4】:对 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;
【讨论】:
ze
是一个目录,您需要跳过“仅文件”操作。尝试执行这些操作会导致异常。
这个答案不应该工作,因为它不会创建丢失的文件来写入数据!!
实际上,如果zip文件是在没有垃圾路径的情况下创建的,则此代码将不起作用,例如,您可以运行此代码来解压缩一个APK文件,您会得到FileNotFoundException。【参考方案5】:
虽然这里已有的答案运行良好,但我发现它们比我希望的要慢一些。相反,我使用了zip4j,我认为这是最好的解决方案,因为它的速度很快。它还允许不同的压缩量选项,我发现这很有用。
【讨论】:
【参考方案6】: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;
->Helper 类(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_
android:layout_
tools:context=".MainActivity" >
<TextView
android:id="@+id/main_status"
android:layout_
android:layout_
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="@string/hello_world" />
</RelativeLayout>
->Menifest 文件中的权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
【讨论】:
【参考方案7】:根据 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
- 这是一个可以在任何地方使用的静态实用方法。
2 File
参数,因为String
是:/ 用于文件,并且之前无法指定要提取 zip 文件的位置。还有path + filename
串联 > https://***.com/a/412495/995891
throws
- 因为 catch late - 如果真的对它们不感兴趣,请添加 try catch。
实际上确保在所有情况下都存在所需的目录。并非每个 zip 都在文件条目之前包含所有必需的目录条目。这有两个潜在的错误:
如果 zip 包含一个空目录,而不是生成的目录有一个现有文件,则忽略此问题。 mkdirs()
的返回值很重要。
在不包含目录的 zip 文件上可能会崩溃。
增加了写入缓冲区的大小,这应该会稍微提高性能。存储通常以 4k 块为单位,以较小的块写入通常比必要的要慢。
使用finally
的魔力来防止资源泄漏。
所以
unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));
应该和原来的一样
unpackZip("/sdcard/", "pictures.zip")
【讨论】:
你好,我正在使用像 sdcard/temp/768\769.json 这样的反斜杠获取路径,所以我遇到了错误,你能告诉我如何管理它 @AndoMasahashi 应该是 linux 文件系统上的合法文件名。你得到什么错误,文件名最后应该是什么样子? 它看起来像 /sdcard/pictures\picturess.jpeg 和错误文件未找到错误 它工作正常,但是当 zip 中的文件名之一不在UTF8 format
中时会引发异常。所以,我使用了this code,而不是使用apache的commons-compress
lib。
@AshishTanna 确实,这是一个已知问题blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in【参考方案8】:
根据@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();
【讨论】:
【参考方案9】:使用下面的类
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 错误【参考方案10】:我用来将特定文件从我的 zipfile 解压缩到我的应用程序缓存文件夹中的最小示例。然后我使用不同的方法读取清单文件。
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();
【讨论】:
【参考方案11】:受密码保护的 Zip 文件
如果你想用密码压缩文件,你可以看看this library,它可以很容易地用密码压缩文件:
邮编:
ZipArchive zipArchive = new ZipArchive();
zipArchive.zip(targetPath,destinationPath,password);
解压:
ZipArchive zipArchive = new ZipArchive();
zipArchive.unzip(targetPath,destinationPath,password);
Rar:
RarArchive rarArchive = new RarArchive();
rarArchive.extractArchive(file archive, file destination);
这个库的文档已经足够好了,我只是从那里添加了一些示例。 它是完全免费的,专门为 android 编写的。
【讨论】:
【参考方案12】: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)
用法
val zipFile = File("path_to_your_zip_file")
file.unzip()
【讨论】:
它在 'output.outputStream() ' 行抛出一个异常,上面写着“FileNotFoundException ...(不是目录)”【参考方案13】:我正在处理 Java 的 ZipFile 类无法处理的 zip 文件。 Java 8 显然无法处理压缩方法 12(我相信是 bzip2)。在尝试了包括 zip4j 在内的多种方法后(由于另一个问题,这些特定文件也失败了),我使用支持 additional compression methods as mentioned here 的 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'
【讨论】:
【参考方案14】:根据 zapl 的回答,在 Closeable
周围添加 try()
会在使用后自动关闭流。
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
使用来自 C# .NET 4
Stream.CopyTo 的 Constant.DefaultBufferSize
(65536
) 来自 Jon Skeet 的回答:
https://***.com/a/411605/1876355
我总是只看到使用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
的倍数。
【讨论】:
这是此线程中的最佳解决方案。此外,我还会在堆栈中使用 BufferedOutputStream 和 FileOutputStream。【参考方案15】:这里是@arsent 解决方案的更简洁版本:
fun File.unzip(to: File? = null)
val destinationDir = to ?: File(parentFile, nameWithoutExtension)
destinationDir.mkdirs()
ZipFile(this).use zipFile ->
zipFile
.entries()
.asSequence()
.filter !it.isDirectory
.forEach zipEntry ->
val currFile = File(destinationDir, zipEntry.name)
currFile.parentFile?.mkdirs()
zipFile.getInputStream(zipEntry).use input ->
currFile.outputStream().use output -> input.copyTo(output)
【讨论】:
以上是关于如何在 Android 中以编程方式解压缩文件?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 ubuntu 14.04 上运行的 ruby 中以编程方式解压缩 .tar.xz 文件(没有中间体)?