android - 如何使用存储卷 (/StorageAccessFramework) 解压缩文件?
问题描述
我正在使用此代码(Android 7.0/Nougat)解压缩外部存储中的 zip 文件(包括内部的多个文件夹级别):
try {
ZipFile zip = new ZipFile(zippath);
Enumeration enu = zip.entries();
while(enu.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry) enu.nextElement();
BufferedInputStream bis = null;
String fileName = null;
try {
fileName = zipEntry.getName();
fileName = fileName.replace("\\",File.separator).replace("/",File.separator);
int p = fileName.lastIndexOf(File.separator);
if(p>=0) {
File fd=new File(folderpath+File.separator+fileName.substring(0,p));
fd.mkdirs();
}
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(folderpath+File.separator+fileName));
bis = new BufferedInputStream(zip.getInputStream(zipEntry));
byte[] buffer = new byte[10000];
int len = 0;
while ((len = bis.read(buffer, 0, 10000)) > 0) {
bos.write(buffer, 0, len);
}
bis.close();
bos.close();
} catch (IOException e1) {
e1.printStackTrace();
return;
}
}
} catch (IOException e2) {
e2.printStackTrace();
}
为了获得对我正在使用的 SD 卡createAccessIntent
(存储卷)的写访问权限,它使用DocumentFile
s 而不是普通的File
.
我已经这样做以获得ZipInputStream
:
InputStream inputStream = this.getContentResolver().openInputStream(myDocumentFileZip.getUri());
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
ZipInputStream zipInputStream = new ZipInputStream(bufferedInputStream);
ZipEntry zipEntry;
...我猜你会继续这样:
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
但是你从那里做什么 - 你如何将文件复制到 SD 卡上并仍然像上面的代码一样保持文件夹结构,但使用存储卷(或存储访问框架)提供的内容?
解决方案
使用存储卷解压缩:
小心:如果您多次解压缩同一个 .zip 文件,它会创建副本,而我在第一篇文章中的原始代码(不能用于 SD 卡)不会,而是会自动覆盖!
try {
InputStream is = getContentResolver().openInputStream(myZip.getUri());
BufferedInputStream bis = new BufferedInputStream(is);
ZipInputStream zis = new ZipInputStream(bis);
ZipEntry zipEntry;
while ((zipEntry = zis.getNextEntry()) != null) {
String fileName = null;
try {
fileName = zipEntry.getName();
fileName = fileName.replace("\\",File.separator).replace("/",File.separator);
int p=fileName.lastIndexOf(File.separator);
DocumentFile destFolder = myDestFolder; //DocumentFile of the destination folder
String destName = fileName;
if (p>=0) {
String[] split = fileName.split(File.separator);
//If the .zip file contains multiple folder levels, this is where you
//have to check and then create them, e.g. for 3 levels:
if(split.length==1) {
destFolder = myFolder;
destName = filename;
} else if(split.length==2) {
if(mySubFolder==null) {
mySubFolder = myFolder.createDirectory(split[0]);
}
destFolder = mySubFolder;
destName = split[1];
} else if(split.length==3) {
if(mySubFolder==null) {
mySubFolder = myFolder.createDirectory(split[0]);
}
if(mySubSubFolder==null) {
mySubSubFolder = mySubFolder.createDirectory(split[1]);
}
destFolder = mySubSubFolder;
destName = split[2];
}
}
DocumentFile df = null;
//Now you have to tell it what file extensions ("MIME" type) you want to use, e.g.:
if(destName.endsWith(".txt")) {
df = destFolder.createFile("text/plain",destName.substring(0,destName.length()-4));
} else if(destName.endsWith(".jpg")) {
df = destFolder.createFile("image/jpeg",destName.substring(0,destName.length()-4));
}
OutputStream out = getContentResolver().openOutputStream(df.getUri());
BufferedOutputStream bos = new BufferedOutputStream(out);
long zipfilesize = zipEntry.getSize();
byte[] buffer = new byte[10000];
int len = 0;
int totlen = 0;
while (((len = zis.read(buffer, 0, 10000)) > 0) && (totlen < zipfilesize)) {
bos.write(buffer, 0, len);
totlen += len;
}
bos.close();
} catch (IOException e1) {
e1.printStackTrace();
return;
}
}
is.close();
bis.close();
zis.close();
} catch (IOException e2) {
e2.printStackTrace();
}
编辑:重要:java.util.zip
不设置size
或compressedSize
(将返回“-1”),这就是为什么此代码只会使用库创建的 zip 文件创建大小为 0B 的文件 - 手动创建的 zip 文件(例如使用 WinRar)工作正常。要修复它,请更换
while (((len = zis.read(buffer, 0, 10000)) > 0) && (totlen < zipfilesize)) {
和
while (((len = zis.read(buffer, 0, 10000)) > 0)) {
这样做是可能的,因为:
对 ZipInputStream.getNextEntry() 的调用将 InputStream 定位在条目的开头,因此提供 ZipInputStream 等效于提供 ZipEntry 的 InputStream。
来源:https ://stackoverflow.com/a/3233600/2016165
这样做的缺点(与我的非 StorageVolume 版本相比)是您 a)无法获取 zip 中的文件总量,并且 b)也无法获取文件的(总)大小,这意味着除非您首先遍历所有 zip 条目以计算它们,否则您无法在“解压缩...”对话框中设置进度条。
推荐阅读
- ansible - 循环文件以检查参数是否存在,如果不存在,则将其添加为默认值
- php - 如何在 Pivot 模型上应用范围?
- python - 如何使用 discord.py 中的事件禁止/踢用户?
- bash - 查找不包含名为 X 的文件的子目录
- mysql - 是否可以使用 sequelize 迁移设置字符集?
- wpf - 如何将绑定属性与 WPF/XAML 中标签内容的字符串连接?
- reactjs - 无法使用 Nightmare.js
- reactjs - 我如何创建和获取我的 mongodb atlas 凭据和 JWT 密钥
- scalability - 冈瑟的“不一致带来的负回报”是什么意思?
- python - 在 Django 项目中使用 Postgres 角色和权限