Chinaunix首页 | 论坛 | 博客
  • 博客访问: 8011
  • 博文数量: 4
  • 博客积分: 200
  • 博客等级: 二等列兵
  • 技术积分: 50
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-19 15:11
文章分类

全部博文(4)

文章存档

2010年(4)

我的朋友
最近访客

分类: 系统运维

2010-01-21 11:09:07

要实现对ZIP文件的解析,首先我们需要来了解一下ZIP文件的数据结构。
简单的说,一个 ZIP 文件的普通格式由三个部分组成: 压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志。

1、压缩源文件数据区
在这个数据区中每一个压缩的源文件/目录都是一条记录,记录的格式如下:
[文件头+ 文件数据 + 数据描述符]
a、文件头结构
组成   长度
文件头标记 4 bytes (0×04034b50)
解压文件所需 pkware 版本 2 bytes
全局方式位标记 2 bytes
压缩方式 2 bytes
最后修改文件时间 2 bytes
最后修改文件日期 2 bytes
CRC-32校验 4 bytes
压缩后尺寸 4 bytes
未压缩尺寸 4 bytes
文件名长度 2 bytes
扩展记录长度 2 bytes
文件名 (不定长度)
扩展字段 (不定长度)
b、文件数据
c、数据描述符
组成  长度
CRC-32校验 4 bytes
压缩后尺寸 4 bytes
未压缩尺寸 4 bytes
这个数据描述符只在全局方式位标记的第3位设为1时才存在(见后详解),紧接在压缩数据的最后一个字节后。这个数据描述符只用在不能对输出的 ZIP 文件进行检索时使用。例如:在一个不能检索的驱动器(如:磁带机上)上的 ZIP 文件中。如果是磁盘上的ZIP文件一般没有这个数据描述符。
2、压缩源文件目录区
在这个数据区中每一条纪录对应在压缩源文件数据区中的一条数据
组成   长度
目录中文件文件头标记 4 bytes (0×02014b50)
压缩使用的 pkware 版本 2 bytes
解压文件所需 pkware 版本 2 bytes
全局方式位标记 2 bytes
压缩方式 2 bytes 10
最后修改文件时间 2 bytes 12
最后修改文件日期 2 bytes 14
CRC-32校验 4 bytes 18
压缩后尺寸 4 bytes 22
未压缩尺寸 4 bytes 26
文件名长度 2 bytes 28
扩展字段长度 2 bytes 30
文件注释长度 2 bytes 32
磁盘开始号 2 bytes 34
内部文件属性 2 bytes 36
外部文件属性 4 bytes 38
局部头部偏移量 4 bytes 42
文件名 (不定长度) 46
扩展字段 (不定长度)
文件注释 (不定长度)
3、压缩源文件目录结束标志
组成   长度
目录结束标记 4 bytes (0×06054b50)
当前磁盘编号 2 bytes 4
目录区开始磁盘编号 2 bytes 6
本磁盘上纪录总数 2 bytes 8
目录区中纪录总数 2 bytes 10
目录区尺寸大小 4 bytes 12
目录区对第一张磁盘的偏移量 4 bytes 16
ZIP 文件注释长度 2 bytes 20
ZIP 文件注释 (不定长度)
下面是实现代码:

  1. xml version="1.0" encoding="utf-8"?>
  2. <mx:Window xmlns:mx="" title="分析ZIP文件" layout="vertical" width="500" height="300" 
  3. creationComplete="init();">
  4. <mx:Script>
  5. import flash.filesystem.*;
  6. import flash.utils.ByteArray;
  7. import flash.events.Event;
  8. private var bytes:ByteArray = new ByteArray();
  9. //定义用来存储文件标头中元数据的变量 variables for reading fixed portion of file header
  10. private var fileName:String = new String();
  11. private var flNameLength:uint;
  12. private var xfldLength:uint;
  13. private var offset:uint;
  14. private var compSize:uint;
  15. private var uncompSize:uint;
  16. private var compMethod:int;
  17. private var signature:int;
  18. private var k:int;
  19. //定义用来表示 .zip 文件的 File (zfile) 和 FileStream (zStream) 对象,并指定将从中提取文件的 .zip 文件的位置(桌面目录
  20. //下名为“HelloAIR.zip” 的文件)。
  21. // File variables for accessing .zip file
  22. private var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
  23. private var zStream:FileStream = new FileStream();
  24. private function init():void
  25. {
  26.     var screenBounds:Rectangle = Screen.mainScreen.bounds;   
  27.         nativeWindow.x = (screenBounds.width - nativeWindow.width) / 2;
  28.         nativeWindow.y = (screenBounds.height - nativeWindow.height) / 2;   
  29. }
  30. private function readout():void
  31. {
  32.     zStream.open(zfile, FileMode.READ);
  33.     bytes.endian = Endian.LITTLE_ENDIAN;
  34.     while (zStream.position < zfile.size)
  35.     {
  36.             //前 30 个字节组成了第一个文件标头的固定大小部分。
  37.         // read fixed metadata portion of local file header
  38.         zStream.readBytes(bytes, 0, 30);
  39.         bytes.position = 0;       
  40.         signature = bytes.readInt();       
  41.         // 再没有可提取的文件, quit
  42.         if (signature != 0x04034b50)//文件开始标记
  43.         {               
  44.             break;
  45.         }
  46.        
  47.         bytes.position = 8;
  48.         compMethod = bytes.readByte(); // store compression method (8 == Deflate)压缩算法 通常是8,没压缩的是0
  49.         offset = 0;// stores length of variable portion of metadata
  50.         bytes.position = 26; // offset to file name length
  51.         flNameLength = bytes.readShort();// store file name
  52.         trace("flNameLength="+flNameLength);
  53.         offset += flNameLength; // add length of file name
  54.         bytes.position = 28;// offset to extra field length
  55.         xfldLength = bytes.readShort();
  56.         trace("xfldLength="+xfldLength);
  57.         offset += xfldLength;// add length of extra field
  58.         // 接下来程序将读取文件标头的可变长度部分,以将该部分的字节数存储在 offset 变量中。
  59.         zStream.readBytes(bytes, 30, offset);
  60.        
  61.         bytes.position = 30;
  62.         fileName = bytes.readMultiByte(flNameLength,"gbk"); // read file name
  63.        
  64.         taFiles.text += fileName + "\n"; // write file name to text area
  65.         bytes.position = 18;
  66.         compSize = bytes.readUnsignedInt(); // store size of compressed portion
  67.         taFiles.text += "\tCompressed size is: " + compSize + '\n';
  68.         bytes.position = 22; // offset to uncompressed size
  69.         uncompSize = bytes.readUnsignedInt(); // store uncompressed size
  70.         taFiles.text += "\tUncompressed size is: " + uncompSize + '\n';
  71.         //将文件的其余部分按照压缩后大小所指定的长度读入 ,未压缩的长度会等于压缩的,所以长度是对的
  72.         zStream.readBytes(bytes, 0, compSize);
  73.         if (compMethod == 8) // if file is compressed, uncompress
  74.         {
  75.             bytes.uncompress(CompressionAlgorithm.DEFLATE);
  76.         }
  77.         outFile(fileName, bytes);       
  78.     } // end of while loop
  79.     var flen:int=0;
  80.     var externlen:int=0;
  81.     var showlen:int=0;
  82.     while (zStream.position < zfile.size)//目录开始标记为0x02014b50
  83.     {
  84.         if(signature != 0x02014b50)
  85.         break;
  86.         bytes.position = 10;
  87.         compMethod = bytes.readByte();       
  88.         bytes.position = 28;
  89.         flen= bytes.readByte();
  90.         trace("bytes.path len:"+flen);
  91.         zStream.readBytes(bytes, 0, 46-30);
  92.         bytes.position = 0;
  93.         externlen=bytes.readByte();
  94.         trace("bytes.externlen len:"+externlen);
  95.         bytes.position = 2;
  96.         showlen=bytes.readByte();
  97.         trace("bytes.showlen len:"+showlen);
  98.         zStream.readBytes(bytes, 0, flen+externlen+showlen);
  99.         if((zStream.position+30)
  100.         zStream.readBytes(bytes, 0, 30);
  101.         bytes.position = 0;       
  102.         signature = bytes.readInt();
  103.         }else{                       
  104.         break;
  105.         }
  106.     }
  107.     zStream.readBytes(bytes, 0, 22);
  108.     bytes.position = 0;       
  109.     signature = bytes.readInt();
  110.     if(signature == 0x06054b50){//目录结束标志
  111.         bytes.position = 20;
  112.         showlen=bytes.readByte();
  113.         trace("bytes.show len="+showlen);
  114.         zStream.readBytes(bytes, 22, showlen);
  115.         bytes.position = 22;       
  116.         fileName=bytes.readMultiByte(showlen,"gbk");               
  117.         taFiles.text+="\n注释:"+fileName;
  118.     }   
  119. } // end of init() method
  120. private function outFile(fileName:String, data:ByteArray):void
  121. {
  122.     var outFile:File = File.desktopDirectory; // dest folder is desktop
  123.     outFile = outFile.resolvePath(fileName); // name of file to write
  124.     var outStream:FileStream = new FileStream();
  125.     // open output file stream in WRITE mode
  126.     outStream.open(outFile, FileMode.WRITE);
  127.     // write out the file
  128.     outStream.writeBytes(data, 0, data.length);
  129.     // close it
  130.     outStream.close();
  131. }
  132. private function selectTextFile(root:File):void
  133.     {
  134.         taFiles.text="";
  135.         var txtFilter:FileFilter = new FileFilter("Zip", "*.Zip;*.zip");
  136.         root.browseForOpen("Open", [txtFilter]);
  137.         root.addEventListener(Event.SELECT, fileSelected);
  138.     }
  139.     private function fileSelected(event:Event):void
  140.     {
  141.         msg.text="读文件:"+event.target.nativePath;
  142.     }
  143. // The application code goes here
  144. ]]>
  145. mx:Script>
  146. <mx:Label id="msg" text="只是测试了不包含子目录的几个文件压缩的zip"/><mx:Button label="文件" click="selectTextFile(zfile)" />
  147. <mx:TextArea id="taFiles" width="320" height="150"/>
  148. <mx:Button label="分析" click="readout()" />
  149. mx:Window>
阅读(366) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~