问题描述
我如何添加文件到现有的压缩文件?我已经有code,可以创建一个zip文件,它的伟大工程,除了一个大问题。它现在的工作方式,用户需要一堆图片,并在结束时,所有的图片被添加到压缩文件,这可能需要相当长一段时间,如果你需要足够的照片。 :-(所以我在想,我有一个很好的和有效的解决方案。随着拍摄照片,我会简单地添加的每一个新的图片它采取右后的zip文件。然后,当他们完成拍照,完成了对ZIP文件,因此它的实用和导出。:-)
现在的问题是,我不能让它将文件添加到现有的zip文件。 :-(这是我有这么远。同时,请记住,这是概念只是一个证明,我明白了重新初始化家居的for循环是非常愚蠢的每次迭代,循环的每次迭代应该重新present添加其他文件,这将很可能是一个漫长的时间后,甚至一小时后,这就是为什么我把一切都重新每次迭代,因为该应用程序将被关闭添加文件之间。如果我能得到这个工作的话,我实际上将沟的for循环,并把这个code到被调用的图片被采取了一切时间功能: - )
尝试{
的for(int i = 0; I< _files.size();我++){
//进行初始设置的东西开始
的BufferedInputStream起源= NULL;
FileOutputStream中DEST =新的FileOutputStream(_zipFile,假);
ZipOutputStream了=新ZipOutputStream(新的BufferedOutputStream(DEST));
字节的数据[] =新的字节[BUFFER]
out.setLevel(0); //我说这是因为它使得它不是COM preSS数据
//在所有的,我希望它将使压缩附加到
//进行初始设置的东西到底
//旧的循环开始
Log.v(的COM preSS,添加+ _FILES [I]);
的FileInputStream F1 =新的FileInputStream(_FILES [I]);
起源=新的BufferedInputStream(FI,BUFFER);
ZipEntry的入门=新的ZipEntry(_FILES [I] .substring(_FILES [I] .lastIndexOf(/)+ 1));
out.putNextEntry(入口);
诠释计数;
而((计数= origin.read(数据,0,缓冲液))!= - 1){
out.write(数据,0,计数);
}
origin.close();
//为老循环结束
//整理东西开始
out.close();
//整理东西到底
}
}赶上(例外五){
Log.e(ZipCreation,写入拉链,E);
e.printStackTrace();
}
另外,我已经尝试用左右。
的FileOutputStream DEST =新的FileOutputStream(_zipFile,真正的);
如果你发现,我设置追加为true,这将实际的数据追加到现有文件。而有趣的是,它实际上不过将数据追加到原始文件,经过文件被提取我的电脑上,写的最后一个文件是所有被提取,这是不好的。 :-(那么有没有一些方法来开始写一个zip文件,再后来,就加入它,并完成了zip文件?我甚至想过有可能采取ZipOutputStream并修改它以适应这种模式,我需要它理应有可能以某种方式: - )
在此先感谢您的帮助! - D
-Jared
好了,感谢您的建议,但我能得到它的工作像我想....是可以做到的,你可以添加文件后,关闭文件,只要你保存你的地方! - D
下面是我如何能得到它打算工作:
尝试{
的for(int i = 0; I< _files.size();我++){
//进行初始设置的东西开始
的BufferedInputStream起源= NULL;
FileOutputStream中DEST =新的FileOutputStream(_zipFile,真正的);
ZipOutputStreamNew OUT =新ZipOutputStreamNew(新的BufferedOutputStream(DEST));
字节的数据[] =新的字节[BUFFER]
如果(有previousData){
out.setWritten(tempWritten);
out.setXentries(tempXentries);
}
//进行初始设置的东西到底
// for循环开始
Log.i(的COM preSS,添加+ _files.get(一));
的FileInputStream F1 =新的FileInputStream(_files.get(一));
起源=新的BufferedInputStream(FI,BUFFER);
TempString = _files.get(ⅰ).substring(_files.get(ⅰ).lastIndexOf(/)+1);
ZipEntry的条目=新的ZipEntry(_paths.get(ⅰ)+ TempString);
out.putNextEntry(入口);
诠释计数;
而((计数= origin.read(数据,0,缓冲液))!= - 1){
out.write(数据,0,计数);
}
origin.close();
out.closeEntry();
// for循环结束
//整理东西开始
如果(ⅰ==(_files.size() - 1)){
//这是最后一条记录,所以我们要完成它关闭
out.closeAndFinish();
} 其他 {
//关闭文件,但不写的中央目录
//首先,备份所在的zip文件是...
tempWritten = out.getWritten();
tempXentries = out.getXentries();
有previousData = TRUE;
//现在关闭文件
out.close();
}
//整理东西到底
}
// ZIP成功
}赶上(例外五){
Log.e(ZipCreation,写入拉链,E);
e.printStackTrace();
}
另外,要记住,这不是唯一的code我不得不这样做。我也不得不让我自己ZipOutputStream副本,这样我可以公开,我在我的ZipOutputStreamNew类....
创建了以下功能 getWritten()
getXentries()
以及
setWritten(长mWritten)
setXentries(矢量< XEntry> mXEntries)
有关的大多数情况下,这一切呢,是它开始写像正常的话,不要关像正常的,它备份这两个变量,然后再进行下一次迭代,它恢复只是那些变量。对>
让我知道,如果您有任何关于这一切有任何疑问,但我知道它会工作,所有它做的是保存在哪里。 - D
再次感谢所有帮助大家的! : - )
在拉吉的要求,这里是源$ C $下ZipOutputStreamNew:
/ **
*这个类实现输出流过滤器在写入文件
* ZIP文件格式。包括两个COM pressed和uncom pressed支持
*条目。
*
* @author大卫·康奈利
* @version%I%,%G%
* /
公共类ZipOutputStreamNew扩展DeflaterOutputStream实现ZipConstants {
公共静态类XEntry {
公众最后的ZipEntry进入;
公众最终长偏移;
公众最终诠释标志;
公共XEntry(ZipEntry的入口,长偏移){
this.entry =条目;
this.offset =抵消;
this.flag =(entry.getMethod()== DEFLATED&安培;&安培;
(entry.getSize()== -1 ||
entry.getCom pressedSize()== -1 ||
entry.getCrc()== - 1))
//店面大小,COM pressed大小和CRC-32的数据描述符
//立即COM pressed项数据如下
? 8
//店面大小,COM pressed大小和CRC-32的LOC头
:0;
}
}
私人XEntry电流;
私人矢量< XEntry> xentries =新矢量< XEntry>();
私人的HashSet<字符串>名称=新的HashSet<字符串>();
私人CRC32 CRC =新CRC32();
私人长书面= 0;
私人长locoff = 0;
私人字符串评论;
私人诠释方法= DEFLATED;
私人布尔完成;
私人布尔闭合= FALSE;
私人布尔closeItPermanently = FALSE;
私有静态诠释的版本(ZipEntry的E)抛出{抛出:ZipException
开关(e.getMethod()){
案例DEFLATED:回归20;
案例存储位置:回归10;
默认:抛出新的抛出:ZipException(不支持COM pression法);
}
}
/ **
*检查,以确保该流仍未关闭。
* /
私人无效ensureOpen()抛出IOException异常{
如果(关闭){
抛出新的IOException异常(流闭);
}
}
/ **
*的COM pression方法pssed(存储)项uncom $ P $。
* /
公共静态最终诠释存储的= ZipEntry.STORED;
/ **
*的COM pression方法COM pressed(DEFLATED)项。
* /
公共静态最终诠释DEFLATED = ZipEntry.DEFLATED;
/ **
*创建一个新的ZIP输出流。
* @Param出实际的输出流
* /
公共ZipOutputStreamNew(OutputStream中出){
超(满分,新Deflater(Deflater.DEFAULT_COM preSSION,真));
usesDefaultDeflater = TRUE;
}
/ **
*设置ZIP文件注释。
* @参数注释注释字符串
抛出:IllegalArgumentException如果指定的长度
* ZIP文件注释大于0xFFFF的字节
* /
公共无效setComment(字符串评论){
如果(注释=空&放大器;!&安培; comment.length()>为0xFFFF / 3
&功放;&安培; getUTF8Length(评论)> 0xFFFF的){
抛出新抛出:IllegalArgumentException(ZIP文件注释太长。);
}
this.comment =意见;
}
/ **
*设置默认的COM pression方法用于后续条目。本
*每当未指定COM pression方法将使用默认值
*为单个ZIP文件条目,并且最初设置为放气。
*参数方法的默认COM pression方法
抛出:IllegalArgumentException如果指定的COM pression方法
* 是无效的
* /
公共无效使用setMethod(INT法){
如果(方法= DEFLATED和放大器;!&安培;!方法=存储){
抛出新抛出:IllegalArgumentException(无效的COM pression法);
}
this.method =方法;
}
/ **
*设置后续的DEFLATED项的COM pression水平。
*默认设置为DEFAULT_COM preSSION。
*参数级别的COM pression级别(0-9)
抛出:IllegalArgumentException如果COM pression级别无效
* /
公共无效执行setLevel(INT级){
def.setLevel(水平);
}
/ **
*开始写入新的ZIP文件条目并将流定位到
*入口数据的开始。关闭当前条目;如果仍然有效。
*默认的COM pression方法将被使用,如果没有COM pression方法
*为条目指定,和当前时间,如果使用
*该条目没有设置修改时间。
*参数êZIP条目被写入
抛出:如果抛出:ZipException发生ZIP格式错误
抛出:IOException - 如果发生I / O错误
* /
公共无效putNextEntry(ZipEntry的E)抛出IOException异常{
ensureOpen();
如果(电流!= NULL){
closeEntry(); //接近previous进入
}
如果(e.getTime()== - 1){
e.setTime(System.currentTimeMillis的());
}
如果(e.getMethod()== - 1){
e.setMethod(法); //使用默认的方法
}
开关(e.getMethod()){
案例DEFLATED:
打破;
案例存储位置:
// COM pressed大小,uncom pressed大小和CRC-32都必须
//为使用存储玉米pression方法条目设置
如果(e.getSize()== - 1){
e.setSize(e.getCom pressedSize());
}否则,如果(e.getCom pressedSize()== -1){
e.setCom pressedSize(e.getSize());
}否则如果(e.getSize()!= e.getCom pressedSize()){
抛出新的抛出:ZipException(
存储条目,其中COM pressed = pssed uncom $ P $大小!);
}
如果(e.getSize()== -1 || e.getCrc()== - 1){
抛出新的抛出:ZipException(
存储的项目缺少大小,COM pressed大小,或CRC-32);
}
打破;
默认:
抛出新的抛出:ZipException(不支持COM pression法);
}
如果(!names.add(e.getName())){
抛出新的抛出:ZipException(重复的条目:+ e.getName());
}
目前=新XEntry(即书面);
xentries.add(电流);
writeLOC(电流);
}
/ **
*关闭当前ZIP条目并写入流定位
*下一条目。
抛出:如果抛出:ZipException发生ZIP格式错误
抛出:IOException - 如果发生I / O错误
* /
公共无效closeEntry()抛出IOException异常{
ensureOpen();
如果(电流!= NULL){
ZipEntry的E = current.entry;
开关(e.getMethod()){
案例DEFLATED:
def.finish();
而(!def.finished()){
放气();
}
如果((current.flag和8)== 0){
//验证大小,COM pressed大小和CRC-32的设置
如果(e.getSize()!= def.getBytesRead()){
抛出新的抛出:ZipException(
无效的条目大小(预期+ e.getSize()+
但有+ def.getBytesRead()+字节));
}
如果(e.getCom pressedSize()!= def.getBytesWritten()){
抛出新的抛出:ZipException(
无效的条目COM pressed尺寸(预期+
e.getCom pressedSize()+,但有+ def.getBytesWritten()+字节));
}
如果(e.getCrc()!= crc.getValue()){
抛出新的抛出:ZipException(
无效的条目CRC-32(预期0X+
Long.toHexString(e.getCrc())+却得到了0X+
Long.toHexString(crc.getValue())+));
}
} 其他 {
e.setSize(def.getBytesRead());
e.setCom pressedSize(def.getBytesWritten());
e.setCrc(crc.getValue());
writeEXT(E);
}
def.reset();
写+ = e.getCom pressedSize();
打破;
案例存储位置:
//我们已经知道,这两个e.size和e.csize是相同的
如果(e.getSize()=笔试 - !locoff){
抛出新的抛出:ZipException(
无效的条目大小(预期+ e.getSize()+
但有+(写 - locoff)+字节));
}
如果(e.getCrc()!= crc.getValue()){
抛出新的抛出:ZipException(
无效的条目CRC-32(预期0X+
Long.toHexString(e.getCrc())+却得到了0X+
Long.toHexString(crc.getValue())+));
}
打破;
默认:
抛出新的抛出:ZipException(无效的COM pression法);
}
crc.reset();
电流=无效;
}
}
/ **
*写一个字节数组到当前的ZIP入口数据。这种方法
*将阻塞,直到所有的字节写入。
* @参数b上被写入数据
*参数关开始的数据偏移
* @参数len个字节被写入的数
抛出:如果抛出:ZipException发生ZIP文件错误
抛出:IOException - 如果发生I / O错误
* /
市民同步无效写入(字节[] B,诠释过,INT LEN)
抛出IOException异常
{
ensureOpen();
如果(关℃,|| LEN℃的||关闭> b.length个 - LEN){
抛出新IndexOutOfBoundsException异常();
}否则,如果(LEN == 0){
返回;
}
如果(当前== NULL){
抛出新的抛出:ZipException(无当前ZIP条目);
}
ZipEntry的入门= current.entry;
开关(entry.getMethod()){
案例DEFLATED:
super.write(B,关闭,LEN);
打破;
案例存储位置:
写+ = LEN;
如果(写 - locoff> entry.getSize()){
抛出新的抛出:ZipException(
尝试写入过去存储的项目结束);
}
out.write(B,关闭,LEN);
打破;
默认:
抛出新的抛出:ZipException(无效的COM pression法);
}
crc.update(B,关闭,LEN);
}
/ **
*完成写入ZIP输出流的内容,无需关闭
*底层流。应用多个过滤器时使用此方法
*在继承到相同的输出流。
抛出:如果抛出:ZipException发生ZIP文件错误
抛出:IOException - 如果发生I / O异常
* /
公共无效结束()抛出IOException异常{
ensureOpen();
如果(成品){
返回;
}
如果(电流!= NULL){
closeEntry();
}
如果(xentries.size()&小于1){
抛出新的抛出:ZipException(ZIP文件必须至少有一个条目);
}
如果(closeItPermanently){
//写入中央目录
龙关=笔试;
对于(XEntry xentry:xentries)
writeCEN(xentry);
writeEND(关,写 - 关);
成品= TRUE;
//Log.e("ZipOutputStreamNew,我只是跑写的中央目录贾里德)!;
}
//Log.e("ZipOutputStreamNew,我只是跑结束()贾里德)!;
}
/ **
*获取xentries变量的值(以供将来使用)
* @返回
* /
公共矢量< XEntry> getXentries(){
返回xentries;
// TODO其转换为原始数据类型
}
/ **
*获取写入变量的值(以备后用)
* @返回
* /
众长getWritten(){
返回写入;
}
/ **
*设定xentries变量的值(以供将来使用)
* @返回
* /
公共无效setXentries(矢量< XEntry> mXEntries){
xentries = mXEntries;
// TODO其转换为原始数据类型
}
/ **
*设置写变量的值(以备后用)
* @返回
* /
公共无效setWritten(长mWritten){
写= mWritten;
}
/ **
*关闭ZIP输出流以及该流被过滤。
抛出:如果抛出:ZipException发生ZIP文件错误
抛出:IOException - 如果发生I / O错误
* /
公共无效closeAndFinish()抛出IOException异常{
如果(!关闭){
closeItPermanently =真;
super.close();
关闭= TRUE;
}
}
/ **
*用于关闭ZIP输出流和正在过滤的流。
*相反,它什么都不做:-P
抛出:如果抛出:ZipException发生ZIP文件错误
抛出:IOException - 如果发生I / O错误
* /
公共无效的close()抛出IOException异常{
如果(!关闭){
closeItPermanently = FALSE;
super.close();
关闭= TRUE;
}
}
/ *
*写入指定的条目本地文件(LOC)头。
* /
私人无效writeLOC(XEntry xentry)抛出IOException异常{
ZipEntry的E = xentry.entry;
INT标志= xentry.flag;
writeInt(LOCSIG); // LOC头签名
与writeShort(版本(E)); //需要提取版本
与writeShort(标志); //通用位标志
与writeShort(e.getMethod()); // COM pression方法
writeInt(e.getTime()); //最后修改时间
如果((标志和8)== 8){
//店面大小,uncom pressed大小和CRC-32的数据描述符
//紧随其后的COM pressed项数据
writeInt(0);
writeInt(0);
writeInt(0);
} 其他 {
writeInt(e.getCrc()); // CRC-32
writeInt(e.getCom pressedSize()); // COM pressed大小
writeInt(e.getSize()); // uncom pressed大小
}
byte []的nameBytes = getUTF8Bytes(e.getName());
与writeShort(nameBytes.length);
与writeShort(!e.getExtra()= NULL e.getExtra()长度:0);
writeBytes(nameBytes,0,nameBytes.length);
如果(e.getExtra()!= NULL){
writeBytes(e.getExtra(),0,e.getExtra()的长度。);
}
locoff =笔试;
}
/ *
*写入额外的数据描述符(EXT)指定的条目。
* /
私人无效writeEXT(ZipEntry的E)抛出IOException异常{
writeInt(EXTSIG); // EXT头签名
writeInt(e.getCrc()); // CRC-32
writeInt(e.getCom pressedSize()); // COM pressed大小
writeInt(e.getSize()); // uncom pressed大小
}
/ *
*写指定入境中央目录(CEN)的头。
*特别提醒:增加对文件属性
* /
私人无效writeCEN(XEntry xentry)抛出IOException异常{
ZipEntry的E = xentry.entry;
INT标志= xentry.flag;
INT版本=(E);
writeInt(CENSIG); //岑头签名
与writeShort(版本); //版本由
与writeShort(版本); //需要提取版本
与writeShort(标志); //通用位标志
与writeShort(e.getMethod()); // COM pression方法
writeInt(e.getTime()); //最后修改时间
writeInt(e.getCrc()); // CRC-32
writeInt(e.getCom pressedSize()); // COM pressed大小
writeInt(e.getSize()); // uncom pressed大小
byte []的nameBytes = getUTF8Bytes(e.getName());
与writeShort(nameBytes.length);
与writeShort(!e.getExtra()= NULL e.getExtra()长度:0);
byte []的commentBytes;
如果(e.getComment()!= NULL){
commentBytes = getUTF8Bytes(e.getComment());
与writeShort(commentBytes.length);
} 其他 {
commentBytes = NULL;
与writeShort(0);
}
与writeShort(0); //启动盘数量
与writeShort(0); //内部文件属性(未使用)
writeInt(0); //外部文件属性(未使用)
writeInt(xentry.offset); //相对本地首部偏移
writeBytes(nameBytes,0,nameBytes.length);
如果(e.getExtra()!= NULL){
writeBytes(e.getExtra(),0,e.getExtra()的长度。);
}
如果(commentBytes!= NULL){
writeBytes(commentBytes,0,commentBytes.length);
}
}
/ *
*写入中央目录(END)头部结束。
* /
私人无效writeEND(龙关,长LEN)抛出IOException异常{
诠释计数= xentries.size();
writeInt(ENDSIG); // END记录签名
与writeShort(0); //这个磁盘的数量
与writeShort(0); //中央目录启动盘
与writeShort(计数); //在磁盘上目录条目的数量
与writeShort(计数); //目录条目总数
writeInt(LEN); //中央目录的长度
writeInt(关闭); //中央目录的偏移
如果(发表评论!= NULL){// ZIP文件注释
byte []的B = getUTF8Bytes(评论);
与writeShort(b.length个);
writeBytes(B,0,b.length个);
} 其他 {
与writeShort(0);
}
}
/ *
*写在小尾数字节顺序一个16位短到输出流。
* /
私人无效与writeShort(int v)按抛出IOException异常{
的OutputStream OUT = this.out;
out.write((V>>大于0)及0xff的);
out.write((V>>→8)及0xff的);
书面+ = 2;
}
/ *
*写入一个32位int在little-endian字节顺序输出流。
* /
私人无效writeInt(长V)抛出IOException异常{
的OutputStream OUT = this.out;
out.write((int)的((V>>大于0)及0xff的));
out.write((int)的((V>>→8)及0xff的));
out.write((int)的((V>>> 16)及0xff的));
out.write((int)的((V>>> 24)及0xff的));
书面+ = 4;
}
/ *
*将字节输出流的阵列。
* /
私人无效writeBytes(byte []的B,诠释过,INT LEN)抛出IOException异常{
super.out.write(B,关闭,LEN);
写+ = LEN;
}
/ *
*返回字符串的UTF8编码的长度。
* /
静态INT getUTF8Length(String s)将{
诠释计数= 0;
的for(int i = 0; I< s.length();我++){
炭CH = s.charAt(ⅰ);
如果(CH< = 0x7F的){
算上++;
}否则如果(CH< = 0x7ff){
数+ = 2;
} 其他 {
数+ = 3;
}
}
返回计数;
}
/ *
*返回重新$ P $字节数组psenting UTF8编码
*指定的字符串。
* /
私有静态的byte [] getUTF8Bytes(String s)将{
的char [] C = s.toCharArray();
INT LEN = c.length;
//计数的EN codeD的字节数...
诠释计数= 0;
的for(int i = 0; I< LEN;我++){
INT CH = C(I);
如果(CH< = 0x7F的){
算上++;
}否则如果(CH< = 0x7ff){
数+ = 2;
} 其他 {
数+ = 3;
}
}
//现在返回EN codeD字节...
byte []的B =新的字节[统计]
诠释关闭= 0;
的for(int i = 0; I< LEN;我++){
INT CH = C(I);
如果(CH< = 0x7F的){
B〔关++] =(字节)通道;
}否则如果(CH< = 0x7ff){
B〔脱++] =(字节)((CH>&→6)|为0xC0);
B〔关++] =(字节)((CH&安培; 0x3F的)| 0x80的);
} 其他 {
B〔关++] =(字节)((CH>> 12)| 0xe0的);
B〔脱++] =(字节)(((CH>→6)及的0x3F)| 0x80的);
B〔关++] =(字节)((CH&安培; 0x3F的)| 0x80的);
}
}
返回b;
}
}
How can I append files to an existing zip file? I already have the code that can create a zip file and it works great except for one big problem. The way it works now, the user takes a bunch of pictures, and at the end, all the pictures get added to a zip file, which can take quite a while if you take enough pictures. :-( So I'm thinking, I have a very good and efficient solution. As the pictures are taken, I will simply add the each new picture to the zip file right after it's taken. Then when they're done taking pictures, finish up the zip file so it's usable and export it. :-)
The problem is, I can not get it to add files to an existing zip file. :-( Here's what I have so far. Also, please keep in mind, this is just a proof of concept, I do understand that re-initializing everything for every iteration of the for loop is very dumb. Each iteration of the loop is supposed to represent another file being added which will most likely be a long time later, maybe even an hour later, which is why I have everything resetting each iteration, because the app will be shut down between adding files. If I can get this working, then I will actually ditch the for loop and put this code into a function that gets called every time a picture gets taken. :-)
try {
for(int i=0; i < _files.size(); i++) {
//beginning of initial setup stuff
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(_zipFile,false);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
byte data[] = new byte[BUFFER];
out.setLevel(0); //I added this because it makes it not compress the data
//at all and I hoped that it would allow the zip to be appended to
//end of initial setup stuff
//beginning of old for loop
Log.v("Compress", "Adding: " + _files[i]);
FileInputStream fi = new FileInputStream(_files[i]);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(_files[i].substring(_files[i].lastIndexOf("/") + 1));
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
//end of for old loop
//beginning of finishing stuff
out.close();
//end of finishing stuff
}
} catch(Exception e) {
Log.e("ZipCreation", "Error writing zip", e);
e.printStackTrace();
}
Also, I have experimented around with
FileOutputStream dest = new FileOutputStream(_zipFile,true);
If you notice, I set append to true, which will actually append the data to an existing file. And what's interesting is, it actually does append the data to the original file, however, after the file gets extracted on my computer, the last file written is all that gets extracted, which is bad. :-( So is there some way to start writing a zip file, and then later, add on to it, and finish up the zip file? I've even thought about possibly taking ZipOutputStream and modifying it to fit this model that I need. It should logically be possible somehow? :-)
Thanks in advance for the help! :-D
-Jared
Ok, thanks for all your suggestions, but I was able to get it working like I wanted.... it CAN be done, you CAN add files after closing the file, as long as you save your place!!! :-D
Here's how I was able to get it going working:
try {
for(int i=0; i < _files.size(); i++) {
//beginning of initial setup stuff
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(_zipFile,true);
ZipOutputStreamNew out = new ZipOutputStreamNew(new BufferedOutputStream(dest));
byte data[] = new byte[BUFFER];
if (havePreviousData) {
out.setWritten(tempWritten);
out.setXentries(tempXentries);
}
//end of initial setup stuff
//beginning of for loop
Log.i("Compress", "Adding: " + _files.get(i));
FileInputStream fi = new FileInputStream(_files.get(i));
origin = new BufferedInputStream(fi, BUFFER);
TempString = _files.get(i).substring(_files.get(i).lastIndexOf("/") + 1);
ZipEntry entry = new ZipEntry(_paths.get(i) + TempString);
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
out.closeEntry();
//end of for loop
//beginning of finishing stuff
if (i == (_files.size()-1)) {
//it's the last record so we should finish it off
out.closeAndFinish();
} else {
//close the file, but don't write the Central Directory
//first, back up where the zip file was...
tempWritten = out.getWritten();
tempXentries = out.getXentries();
havePreviousData = true;
//now close the file
out.close();
}
//end of finishing stuff
}
//zip succeeded
} catch(Exception e) {
Log.e("ZipCreation", "Error writing zip", e);
e.printStackTrace();
}
Also, keep in mind, this is not the only code I had to do. I also had to make my own copy of ZipOutputStream so that I could expose the following functions that I created within my ZipOutputStreamNew class....
getWritten()
getXentries()
as well as
setWritten(long mWritten)
setXentries(Vector<XEntry> mXEntries)
For the most part, all this does, is it starts writing like normal, then, instead of closing like normal, it backs up those two variables, and then for the next iteration, it restores just those variables.
Let me know if you have any questions about all this, but I knew it would work, all it has to do is save where it was. :-D
Thanks again for all the help everybody! :-)
At Raj's request, here is the source code for ZipOutputStreamNew:
/**
* This class implements an output stream filter for writing files in the
* ZIP file format. Includes support for both compressed and uncompressed
* entries.
*
* @author David Connelly
* @version %I%, %G%
*/
public class ZipOutputStreamNew extends DeflaterOutputStream implements ZipConstants {
public static class XEntry {
public final ZipEntry entry;
public final long offset;
public final int flag;
public XEntry(ZipEntry entry, long offset) {
this.entry = entry;
this.offset = offset;
this.flag = (entry.getMethod() == DEFLATED &&
(entry.getSize() == -1 ||
entry.getCompressedSize() == -1 ||
entry.getCrc() == -1))
// store size, compressed size, and crc-32 in data descriptor
// immediately following the compressed entry data
? 8
// store size, compressed size, and crc-32 in LOC header
: 0;
}
}
private XEntry current;
private Vector<XEntry> xentries = new Vector<XEntry>();
private HashSet<String> names = new HashSet<String>();
private CRC32 crc = new CRC32();
private long written = 0;
private long locoff = 0;
private String comment;
private int method = DEFLATED;
private boolean finished;
private boolean closed = false;
private boolean closeItPermanently = false;
private static int version(ZipEntry e) throws ZipException {
switch (e.getMethod()) {
case DEFLATED: return 20;
case STORED: return 10;
default: throw new ZipException("unsupported compression method");
}
}
/**
* Checks to make sure that this stream has not been closed.
*/
private void ensureOpen() throws IOException {
if (closed) {
throw new IOException("Stream closed");
}
}
/**
* Compression method for uncompressed (STORED) entries.
*/
public static final int STORED = ZipEntry.STORED;
/**
* Compression method for compressed (DEFLATED) entries.
*/
public static final int DEFLATED = ZipEntry.DEFLATED;
/**
* Creates a new ZIP output stream.
* @param out the actual output stream
*/
public ZipOutputStreamNew(OutputStream out) {
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
usesDefaultDeflater = true;
}
/**
* Sets the ZIP file comment.
* @param comment the comment string
* @exception IllegalArgumentException if the length of the specified
* ZIP file comment is greater than 0xFFFF bytes
*/
public void setComment(String comment) {
if (comment != null && comment.length() > 0xffff/3
&& getUTF8Length(comment) > 0xffff) {
throw new IllegalArgumentException("ZIP file comment too long.");
}
this.comment = comment;
}
/**
* Sets the default compression method for subsequent entries. This
* default will be used whenever the compression method is not specified
* for an individual ZIP file entry, and is initially set to DEFLATED.
* @param method the default compression method
* @exception IllegalArgumentException if the specified compression method
* is invalid
*/
public void setMethod(int method) {
if (method != DEFLATED && method != STORED) {
throw new IllegalArgumentException("invalid compression method");
}
this.method = method;
}
/**
* Sets the compression level for subsequent entries which are DEFLATED.
* The default setting is DEFAULT_COMPRESSION.
* @param level the compression level (0-9)
* @exception IllegalArgumentException if the compression level is invalid
*/
public void setLevel(int level) {
def.setLevel(level);
}
/**
* Begins writing a new ZIP file entry and positions the stream to the
* start of the entry data. Closes the current entry if still active.
* The default compression method will be used if no compression method
* was specified for the entry, and the current time will be used if
* the entry has no set modification time.
* @param e the ZIP entry to be written
* @exception ZipException if a ZIP format error has occurred
* @exception IOException if an I/O error has occurred
*/
public void putNextEntry(ZipEntry e) throws IOException {
ensureOpen();
if (current != null) {
closeEntry(); // close previous entry
}
if (e.getTime() == -1) {
e.setTime(System.currentTimeMillis());
}
if (e.getMethod() == -1) {
e.setMethod(method); // use default method
}
switch (e.getMethod()) {
case DEFLATED:
break;
case STORED:
// compressed size, uncompressed size, and crc-32 must all be
// set for entries using STORED compression method
if (e.getSize() == -1) {
e.setSize(e.getCompressedSize());
} else if (e.getCompressedSize() == -1) {
e.setCompressedSize(e.getSize());
} else if (e.getSize() != e.getCompressedSize()) {
throw new ZipException(
"STORED entry where compressed != uncompressed size");
}
if (e.getSize() == -1 || e.getCrc() == -1) {
throw new ZipException(
"STORED entry missing size, compressed size, or crc-32");
}
break;
default:
throw new ZipException("unsupported compression method");
}
if (! names.add(e.getName())) {
throw new ZipException("duplicate entry: " + e.getName());
}
current = new XEntry(e, written);
xentries.add(current);
writeLOC(current);
}
/**
* Closes the current ZIP entry and positions the stream for writing
* the next entry.
* @exception ZipException if a ZIP format error has occurred
* @exception IOException if an I/O error has occurred
*/
public void closeEntry() throws IOException {
ensureOpen();
if (current != null) {
ZipEntry e = current.entry;
switch (e.getMethod()) {
case DEFLATED:
def.finish();
while (!def.finished()) {
deflate();
}
if ((current.flag & 8) == 0) {
// verify size, compressed size, and crc-32 settings
if (e.getSize() != def.getBytesRead()) {
throw new ZipException(
"invalid entry size (expected " + e.getSize() +
" but got " + def.getBytesRead() + " bytes)");
}
if (e.getCompressedSize() != def.getBytesWritten()) {
throw new ZipException(
"invalid entry compressed size (expected " +
e.getCompressedSize() + " but got " + def.getBytesWritten() + " bytes)");
}
if (e.getCrc() != crc.getValue()) {
throw new ZipException(
"invalid entry CRC-32 (expected 0x" +
Long.toHexString(e.getCrc()) + " but got 0x" +
Long.toHexString(crc.getValue()) + ")");
}
} else {
e.setSize(def.getBytesRead());
e.setCompressedSize(def.getBytesWritten());
e.setCrc(crc.getValue());
writeEXT(e);
}
def.reset();
written += e.getCompressedSize();
break;
case STORED:
// we already know that both e.size and e.csize are the same
if (e.getSize() != written - locoff) {
throw new ZipException(
"invalid entry size (expected " + e.getSize() +
" but got " + (written - locoff) + " bytes)");
}
if (e.getCrc() != crc.getValue()) {
throw new ZipException(
"invalid entry crc-32 (expected 0x" +
Long.toHexString(e.getCrc()) + " but got 0x" +
Long.toHexString(crc.getValue()) + ")");
}
break;
default:
throw new ZipException("invalid compression method");
}
crc.reset();
current = null;
}
}
/**
* Writes an array of bytes to the current ZIP entry data. This method
* will block until all the bytes are written.
* @param b the data to be written
* @param off the start offset in the data
* @param len the number of bytes that are written
* @exception ZipException if a ZIP file error has occurred
* @exception IOException if an I/O error has occurred
*/
public synchronized void write(byte[] b, int off, int len)
throws IOException
{
ensureOpen();
if (off < 0 || len < 0 || off > b.length - len) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
if (current == null) {
throw new ZipException("no current ZIP entry");
}
ZipEntry entry = current.entry;
switch (entry.getMethod()) {
case DEFLATED:
super.write(b, off, len);
break;
case STORED:
written += len;
if (written - locoff > entry.getSize()) {
throw new ZipException(
"attempt to write past end of STORED entry");
}
out.write(b, off, len);
break;
default:
throw new ZipException("invalid compression method");
}
crc.update(b, off, len);
}
/**
* Finishes writing the contents of the ZIP output stream without closing
* the underlying stream. Use this method when applying multiple filters
* in succession to the same output stream.
* @exception ZipException if a ZIP file error has occurred
* @exception IOException if an I/O exception has occurred
*/
public void finish() throws IOException {
ensureOpen();
if (finished) {
return;
}
if (current != null) {
closeEntry();
}
if (xentries.size() < 1) {
throw new ZipException("ZIP file must have at least one entry");
}
if (closeItPermanently) {
// write central directory
long off = written;
for (XEntry xentry : xentries)
writeCEN(xentry);
writeEND(off, written - off);
finished = true;
//Log.e("ZipOutputStreamNew", "I just ran wrote the Central Directory Jared!");
}
//Log.e("ZipOutputStreamNew", "I just ran finish() Jared!");
}
/**
* Gets the value of the "xentries" variable (for later use)
* @return
*/
public Vector<XEntry> getXentries() {
return xentries;
//TODO convert this to primitive data types
}
/**
* Gets the value of the "written" variable (for later use)
* @return
*/
public long getWritten() {
return written;
}
/**
* Sets the value of the "xentries" variable (for later use)
* @return
*/
public void setXentries(Vector<XEntry> mXEntries) {
xentries = mXEntries;
//TODO convert this to primitive data types
}
/**
* Sets the value of the "written" variable (for later use)
* @return
*/
public void setWritten(long mWritten) {
written = mWritten;
}
/**
* Closes the ZIP output stream as well as the stream being filtered.
* @exception ZipException if a ZIP file error has occurred
* @exception IOException if an I/O error has occurred
*/
public void closeAndFinish() throws IOException {
if (!closed) {
closeItPermanently=true;
super.close();
closed = true;
}
}
/**
* Used to close the ZIP output stream as well as the stream being filtered.
* instead it does nothing :-P
* @exception ZipException if a ZIP file error has occurred
* @exception IOException if an I/O error has occurred
*/
public void close() throws IOException {
if (!closed) {
closeItPermanently=false;
super.close();
closed = true;
}
}
/*
* Writes local file (LOC) header for specified entry.
*/
private void writeLOC(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
int flag = xentry.flag;
writeInt(LOCSIG); // LOC header signature
writeShort(version(e)); // version needed to extract
writeShort(flag); // general purpose bit flag
writeShort(e.getMethod()); // compression method
writeInt(e.getTime()); // last modification time
if ((flag & 8) == 8) {
// store size, uncompressed size, and crc-32 in data descriptor
// immediately following compressed entry data
writeInt(0);
writeInt(0);
writeInt(0);
} else {
writeInt(e.getCrc()); // crc-32
writeInt(e.getCompressedSize()); // compressed size
writeInt(e.getSize()); // uncompressed size
}
byte[] nameBytes = getUTF8Bytes(e.getName());
writeShort(nameBytes.length);
writeShort(e.getExtra() != null ? e.getExtra().length : 0);
writeBytes(nameBytes, 0, nameBytes.length);
if (e.getExtra() != null) {
writeBytes(e.getExtra(), 0, e.getExtra().length);
}
locoff = written;
}
/*
* Writes extra data descriptor (EXT) for specified entry.
*/
private void writeEXT(ZipEntry e) throws IOException {
writeInt(EXTSIG); // EXT header signature
writeInt(e.getCrc()); // crc-32
writeInt(e.getCompressedSize()); // compressed size
writeInt(e.getSize()); // uncompressed size
}
/*
* Write central directory (CEN) header for specified entry.
* REMIND: add support for file attributes
*/
private void writeCEN(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
int flag = xentry.flag;
int version = version(e);
writeInt(CENSIG); // CEN header signature
writeShort(version); // version made by
writeShort(version); // version needed to extract
writeShort(flag); // general purpose bit flag
writeShort(e.getMethod()); // compression method
writeInt(e.getTime()); // last modification time
writeInt(e.getCrc()); // crc-32
writeInt(e.getCompressedSize()); // compressed size
writeInt(e.getSize()); // uncompressed size
byte[] nameBytes = getUTF8Bytes(e.getName());
writeShort(nameBytes.length);
writeShort(e.getExtra() != null ? e.getExtra().length : 0);
byte[] commentBytes;
if (e.getComment() != null) {
commentBytes = getUTF8Bytes(e.getComment());
writeShort(commentBytes.length);
} else {
commentBytes = null;
writeShort(0);
}
writeShort(0); // starting disk number
writeShort(0); // internal file attributes (unused)
writeInt(0); // external file attributes (unused)
writeInt(xentry.offset); // relative offset of local header
writeBytes(nameBytes, 0, nameBytes.length);
if (e.getExtra() != null) {
writeBytes(e.getExtra(), 0, e.getExtra().length);
}
if (commentBytes != null) {
writeBytes(commentBytes, 0, commentBytes.length);
}
}
/*
* Writes end of central directory (END) header.
*/
private void writeEND(long off, long len) throws IOException {
int count = xentries.size();
writeInt(ENDSIG); // END record signature
writeShort(0); // number of this disk
writeShort(0); // central directory start disk
writeShort(count); // number of directory entries on disk
writeShort(count); // total number of directory entries
writeInt(len); // length of central directory
writeInt(off); // offset of central directory
if (comment != null) { // zip file comment
byte[] b = getUTF8Bytes(comment);
writeShort(b.length);
writeBytes(b, 0, b.length);
} else {
writeShort(0);
}
}
/*
* Writes a 16-bit short to the output stream in little-endian byte order.
*/
private void writeShort(int v) throws IOException {
OutputStream out = this.out;
out.write((v >>> 0) & 0xff);
out.write((v >>> 8) & 0xff);
written += 2;
}
/*
* Writes a 32-bit int to the output stream in little-endian byte order.
*/
private void writeInt(long v) throws IOException {
OutputStream out = this.out;
out.write((int)((v >>> 0) & 0xff));
out.write((int)((v >>> 8) & 0xff));
out.write((int)((v >>> 16) & 0xff));
out.write((int)((v >>> 24) & 0xff));
written += 4;
}
/*
* Writes an array of bytes to the output stream.
*/
private void writeBytes(byte[] b, int off, int len) throws IOException {
super.out.write(b, off, len);
written += len;
}
/*
* Returns the length of String's UTF8 encoding.
*/
static int getUTF8Length(String s) {
int count = 0;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch <= 0x7f) {
count++;
} else if (ch <= 0x7ff) {
count += 2;
} else {
count += 3;
}
}
return count;
}
/*
* Returns an array of bytes representing the UTF8 encoding
* of the specified String.
*/
private static byte[] getUTF8Bytes(String s) {
char[] c = s.toCharArray();
int len = c.length;
// Count the number of encoded bytes...
int count = 0;
for (int i = 0; i < len; i++) {
int ch = c[i];
if (ch <= 0x7f) {
count++;
} else if (ch <= 0x7ff) {
count += 2;
} else {
count += 3;
}
}
// Now return the encoded bytes...
byte[] b = new byte[count];
int off = 0;
for (int i = 0; i < len; i++) {
int ch = c[i];
if (ch <= 0x7f) {
b[off++] = (byte)ch;
} else if (ch <= 0x7ff) {
b[off++] = (byte)((ch >> 6) | 0xc0);
b[off++] = (byte)((ch & 0x3f) | 0x80);
} else {
b[off++] = (byte)((ch >> 12) | 0xe0);
b[off++] = (byte)(((ch >> 6) & 0x3f) | 0x80);
b[off++] = (byte)((ch & 0x3f) | 0x80);
}
}
return b;
}
}
这篇关于Android的附加文件到压缩文件,而无需重新写入整个压缩文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!