在android上压缩/压缩一个充满文件的文件夹
Posted
技术标签:
【中文标题】在android上压缩/压缩一个充满文件的文件夹【英文标题】:zip/compress a folder full of files on android 【发布时间】:2011-10-04 17:52:48 【问题描述】:我需要压缩一个“项目”文件夹以允许用户通过电子邮件共享项目。我找到了一个将多个文件压缩到一个 zip 中的类,但我需要将文件夹结构保留在我的 zip 中。有没有办法在android上实现这一点?提前致谢。
【问题讨论】:
澄清一下,你是指Android开发项目还是app中开发的项目? 在应用程序中开发的项目,对此感到抱歉。另外我认为这就是我需要的:***.com/questions/1399126/… 但是当我复制它时,我尽了最大努力,但找不到该行的方法: Deque这段代码应该可以解决问题。
注意:您必须通过将 WRITE_EXTERNAL_STORAGE 权限添加到 manifest.xml 文件来为您的应用添加文件写入权限。
/*
*
* Zips a file at a location and places the resulting zip file at the toLocation
* Example: zipFileAtPath("downloads/myfolder", "downloads/myFolder.zip");
*/
public boolean zipFileAtPath(String sourcePath, String toLocation)
final int BUFFER = 2048;
File sourceFile = new File(sourcePath);
try
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(toLocation);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
dest));
if (sourceFile.isDirectory())
zipSubFolder(out, sourceFile, sourceFile.getParent().length());
else
byte data[] = new byte[BUFFER];
FileInputStream fi = new FileInputStream(sourcePath);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(getLastPathComponent(sourcePath));
entry.setTime(sourceFile.lastModified()); // to keep modification time after unzipping
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1)
out.write(data, 0, count);
out.close();
catch (Exception e)
e.printStackTrace();
return false;
return true;
/*
*
* Zips a subfolder
*
*/
private void zipSubFolder(ZipOutputStream out, File folder,
int basePathLength) throws IOException
final int BUFFER = 2048;
File[] fileList = folder.listFiles();
BufferedInputStream origin = null;
for (File file : fileList)
if (file.isDirectory())
zipSubFolder(out, file, basePathLength);
else
byte data[] = new byte[BUFFER];
String unmodifiedFilePath = file.getPath();
String relativePath = unmodifiedFilePath
.substring(basePathLength);
FileInputStream fi = new FileInputStream(unmodifiedFilePath);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(relativePath);
entry.setTime(file.lastModified()); // to keep modification time after unzipping
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1)
out.write(data, 0, count);
origin.close();
/*
* gets the last path component
*
* Example: getLastPathComponent("downloads/example/fileToZip");
* Result: "fileToZip"
*/
public String getLastPathComponent(String filePath)
String[] segments = filePath.split("/");
if (segments.length == 0)
return "";
String lastPathComponent = segments[segments.length - 1];
return lastPathComponent;
【讨论】:
嘿@HailZeon!很棒的代码,真的很有帮助。 getLastPathComponent(sourcePath) 做什么以及它是如何定义的?谢谢! 嘿@RaymondMachira。我添加了 getLastPathComponent 的定义。它基本上采用路径(“folder1/subfolder/example.txt”)并返回“folder1/subfolder/”。 zip 文件中可能没有定义包含文件夹,因此我们需要删除子路径并添加它。 我在 zip 文件中得到了空的命名文件夹,解决方法是将 sourceFile.getParent().length() 替换为 sourceFile.getParent().length()+1。 你应该用 filePath.substring(filePath.lastIndexOf("/")) 替换 getLastPathComponent(String filePath) 内容 很棒的代码——我正在为我发现的一个错误添加一个修复程序——当尝试在 Windows 上解压缩时,由于解析给 ZipEntry 对象的相对路径上的第一个“/”问题而失败。 ===== 代码修复 ==== String relativePath = unmodifiedFilePath .substring(basePathLength+1);【参考方案2】:这就是我的做法:
private static void zipFolder(String inputFolderPath, String outZipPath)
try
FileOutputStream fos = new FileOutputStream(outZipPath);
ZipOutputStream zos = new ZipOutputStream(fos);
File srcFile = new File(inputFolderPath);
File[] files = srcFile.listFiles();
Log.d("", "Zip directory: " + srcFile.getName());
for (int i = 0; i < files.length; i++)
Log.d("", "Adding file: " + files[i].getName());
byte[] buffer = new byte[1024];
FileInputStream fis = new FileInputStream(files[i]);
zos.putNextEntry(new ZipEntry(files[i].getName()));
int length;
while ((length = fis.read(buffer)) > 0)
zos.write(buffer, 0, length);
zos.closeEntry();
fis.close();
zos.close();
catch (IOException ioe)
Log.e("", ioe.getMessage());
【讨论】:
如果源文件夹包含另一个文件夹,则无法正常工作 压缩给定的文件夹。我压缩了一个充满图像的文件夹,给定的文件夹被压缩了,但是解压缩时文件夹中的图像不可读。怎么可能解决? 如果要解压,只需在文件夹名称末尾添加扩展名.zip,即可轻松解压。【参考方案3】:我已经修改了来自 HailZeon 的代码以在 Windows 下正常工作。 Zip 条目必须在启动新条目之前关闭,并且条目名称(如“/file.txt”)处的起始“/”也会产生问题
/**
* Zips a Folder to "[Folder].zip"
* @param toZipFolder Folder to be zipped
* @return the resulting ZipFile
*/
public static File zipFolder(File toZipFolder)
File ZipFile = new File(toZipFolder.getParent(), format("%s.zip", toZipFolder.getName()));
try
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(ZipFile));
zipSubFolder(out, toZipFolder, toZipFolder.getPath().length());
out.close();
return ZipFile;
catch (Exception ex)
ex.printStackTrace();
return null;
/**
* Main zip Function
* @param out Target ZipStream
* @param folder Folder to be zipped
* @param basePathLength Length of original Folder Path (for recursion)
*/
private static void zipSubFolder(ZipOutputStream out, File folder, int basePathLength) throws IOException
final int BUFFER = 2048;
File[] fileList = folder.listFiles();
BufferedInputStream origin = null;
for (File file : fileList)
if (file.isDirectory())
zipSubFolder(out, file, basePathLength);
else
byte data[] = new byte[BUFFER];
String unmodifiedFilePath = file.getPath();
String relativePath = unmodifiedFilePath.substring(basePathLength + 1);
FileInputStream fi = new FileInputStream(unmodifiedFilePath);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(relativePath);
entry.setTime(file.lastModified()); // to keep modification time after unzipping
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1)
out.write(data, 0, count);
origin.close();
out.closeEntry();
【讨论】:
【参考方案4】:使用来自location 的 zip4j 库。将 jar 文件导入您的 "app/libs/"
文件夹。并使用以下代码压缩您的目录/文件...
try
File input = new File("path/to/your/input/fileOrFolder");
String destinationPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "zippedItem.zip";
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_STORE);
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
File output = new File(destinationPath);
ZipFile zipFile = new ZipFile(output);
// .addFolder or .addFile depending on your input
if (sourceFile.isDirectory())
zipFile.addFolder(input, parameters);
else
zipFile.addFile(input, parameters);
// Your input file/directory has been zipped at this point and you
// can access it as a normal file using the following line of code
File zippedFile = zipFile.getFile();
catch (ZipException e)
Log.e(TAG, Log.getStackTraceString(e));
【讨论】:
【参考方案5】:如果您使用 java.util.zip 对象,那么您可以编写不修改目录结构的脚本。
【讨论】:
【参考方案6】:public static boolean zip(File sourceFile, File zipFile)
List<File> fileList = getSubFiles(sourceFile, true);
ZipOutputStream zipOutputStream = null;
try
zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFile));
int bufferSize = 1024;
byte[] buf = new byte[bufferSize];
ZipEntry zipEntry;
for(int i = 0; i < fileList.size(); i++)
File file = fileList.get(i);
zipEntry = new ZipEntry(sourceFile.toURI().relativize(file.toURI()).getPath());
zipOutputStream.putNextEntry(zipEntry);
if (!file.isDirectory())
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
int readLength;
while ((readLength = inputStream.read(buf, 0, bufferSize)) != -1)
zipOutputStream.write(buf, 0, readLength);
catch (Exception e)
e.printStackTrace();
return false;
finally
IoUtils.closeOS(zipOutputStream);
return true;
public static List<File> getSubFiles(File baseDir, boolean isContainFolder)
List<File> fileList = new ArrayList<>();
File[] tmpList = baseDir.listFiles();
for (File file : tmpList)
if (file.isFile())
fileList.add(file);
if (file.isDirectory())
if (isContainFolder)
fileList.add(file); //key code
fileList.addAll(getSubFiles(file));
return fileList;
【讨论】:
【参考方案7】:只需将@HailZeon 的答案转换为 Kotlin:
import java.io.BufferedInputStream
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.ZipOutputStream
import java.util.zip.ZipException
private const val BUFFER = 2048
/**
* Compresses a file into a zip file
* @author Arnau Mora
* @since 20210318
* @param file The source file
* @param target The target file
*
* @throws NullPointerException If the entry name is null
* @throws IllegalArgumentException If the entry name is longer than 0xFFFF byte
* @throws SecurityException If a security manager exists and its SecurityManager.checkRead(String)
* method denies read access to the file
* @throws ZipException If a ZIP format error has occurred
* @throws IOException If an I/O error has occurre
*/
@Throws(
NullPointerException::class,
IllegalArgumentException::class,
SecurityException::class,
ZipException::class,
IOException::class
)
fun zipFile(file: File, target: File)
val origin: BufferedInputStream
val dest: FileOutputStream
var zipOutput: ZipOutputStream? = null
try
dest = target.outputStream()
zipOutput = ZipOutputStream(dest.buffered(BUFFER))
if (file.isDirectory)
zipSubFolder(zipOutput, file, file.parent!!.length)
else
val data = ByteArray(BUFFER)
val fi = file.inputStream()
origin = fi.buffered(BUFFER)
val entry = ZipEntry(getLastPathComponent(file.path))
entry.time = file.lastModified()
zipOutput.putNextEntry(entry)
var count = origin.read(data, 0, BUFFER)
while (count != -1)
zipOutput.write(data, 0, count)
count = origin.read(data, 0, BUFFER)
finally
zipOutput?.close()
private fun zipSubFolder(zipOutput: ZipOutputStream, folder: File, basePathLength: Int)
val files = folder.listFiles() ?: return
var origin: BufferedInputStream? = null
try
for (file in files)
if (file.isDirectory)
zipSubFolder(zipOutput, folder, basePathLength)
else
val data = ByteArray(BUFFER)
val unmodifiedFilePath = file.path
val relativePath = unmodifiedFilePath.substring(basePathLength)
val fi = FileInputStream(unmodifiedFilePath)
origin = fi.buffered(BUFFER)
val entry = ZipEntry(relativePath)
entry.time = file.lastModified()
zipOutput.putNextEntry(entry)
var count = origin.read(data, 0, BUFFER)
while (count != -1)
zipOutput.write(data, 0, count)
count = origin.read(data, 0, BUFFER)
finally
origin?.close()
/*
* gets the last path component
*
* Example: getLastPathComponent("downloads/example/fileToZip");
* Result: "fileToZip"
*/
private fun getLastPathComponent(filePath: String): String
val segments = filePath.split("/").toTypedArray()
return if (segments.isEmpty()) "" else segments[segments.size - 1]
【讨论】:
【参考方案8】:如果有人来到这里试图找到 @HailZeon 代码的 java8 更清洁的重构版本。这里是:
private static final int BUFFER_SIZE = 2048;
public File zip(File source, String zipFileName) throws IOException
File zipFile = null;
if(source != null)
File zipFileDestination = new File(getCacheDir(), zipFileName);
if (zipFileDestination.exists())
zipFileDestination.delete();
zipFile = zip(source, zipFileDestination);
return zipFile;
public File zip(File source, File zipFile) throws IOException
int relativeStartingPathIndex = zipFile.getAbsolutePath().lastIndexOf("/") + 1;
if (source != null && zipFile != null)
try (ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream( new FileOutputStream(zipFile))))
if (source.isDirectory())
zipSubDir(out, source, relativeStartingPathIndex);
else
try (BufferedInputStream origin = new BufferedInputStream(new FileInputStream(source)))
zipEntryFile(origin, out, source, relativeStartingPathIndex);
return zipFile;
private void zipSubDir(ZipOutputStream out, File dir, int relativeStartingPathIndex) throws IOException
File[] files = dir.listFiles();
if (files != null)
for(File file : files)
if(file.isDirectory())
zipSubDir(out, file, relativeStartingPathIndex);
else
try (BufferedInputStream origin = new BufferedInputStream(new FileInputStream(file)))
zipEntryFile(origin, out, file, relativeStartingPathIndex);
private void zipEntryFile(BufferedInputStream origin, ZipOutputStream out, File file, int relativeStartingPathIndex) throws IOException
String relativePath = file.getAbsolutePath().substring(relativeStartingPathIndex);
ZipEntry entry = new ZipEntry(relativePath);
entry.setTime(file.lastModified()); // to keep modification time after unzipping
out.putNextEntry(entry);
byte[] data = new byte[BUFFER_SIZE];
int count;
while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1)
out.write(data, 0, count);
【讨论】:
以上是关于在android上压缩/压缩一个充满文件的文件夹的主要内容,如果未能解决你的问题,请参考以下文章