이 예제에서는 다양한 유형의 여러 파일이 포함된 간단한 .zip 파일을 읽는 방법을 보여 줍니다. 이 예제에서는 각 파일을 ByteArray에 압축 해제하고 데스크톱에 쓰며 각 파일에 대한 메타데이터에서 관련 데이터를 추출합니다.
.zip 파일의 일반 구조는
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
에서 유지 관리되는 PKWARE Inc. 사양을 기반으로 합니다. .zip 아카이브의 첫 번째 파일에 대한 파일 헤더와 파일 데이터가 먼저 오고 그 다음에는 각 추가 파일에 대한 파일 헤더와 파일 데이터 쌍이 옵니다. 파일 헤더의 구조는 나중에 설명하겠습니다. 다음으로 .zip 파일에는 선택적으로 데이터 설명자 레코드가 포함됩니다(일반적으로 출력 zip 파일이 디스크에 저장된 것이 아니라 메모리에 만들어진 경우). 그 다음으로는 아카이브 암호 해독 헤더, 아카이브 추가 데이터 레코드, 중앙 디렉토리 구조, 중앙 디렉토리 레코드의 Zip64 끝, 중앙 디렉토리 로케이터의 Zip64 끝 및 중앙 디렉토리 레코드의 끝과 같은 여러 추가 선택적 요소가 옵니다.
이 예제의 코드는 폴더가 포함되지 않은 zip 파일을 파싱하기 위한 용도로만 작성되었으며 데이터 설명자 레코드를 필요로 하지 않습니다. 마지막 파일 데이터 다음에 오는 모든 정보는 무시됩니다.
각 파일에 대한 파일 헤더의 형식은 다음과 같습니다.
파일 헤더 서명
|
4바이트
|
필요한 버전
|
2바이트
|
일반 용도의 비트 플래그
|
2바이트
|
압축 방법
|
2바이트(8=DEFLATE, 0=압축 해제)
|
가장 최근에 파일을 수정한 시간
|
2바이트
|
가장 최근에 파일을 수정한 날짜
|
2바이트
|
crc-32
|
4바이트
|
압축된 크기
|
4바이트
|
압축 해제된 크기
|
4바이트
|
파일 이름 길이
|
2바이트
|
추가 필드 길이
|
2바이트
|
파일 이름
|
variable
|
추가 필드
|
variable
|
파일 헤더 다음에는 실제 파일 데이터가 오며 이 데이터는 압축 방법 플래그에 따라 압축하거나 압축 해제할 수 있습니다. 파일 데이터를 압축 해제하는 경우 플래그는 0이며 DEFLATE 알고리즘을 사용하여 데이터를 압축하는 경우 8, 기타 압축 알고리즘의 경우 다른 값입니다.
이 예제에 대한 사용자 인터페이스는 레이블 및 텍스트 영역(
taFiles
)으로 구성됩니다. 응용 프로그램은 .zip 파일에서 발견하는 각 파일에 대해 파일 이름, 압축된 크기 및 압축 해제된 크기와 같은 정보를 텍스트 영역에 씁니다. 다음 MXML 문서는 Flex 버전 응용 프로그램의 사용자 인터페이스를 정의합니다.
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();">
<mx:Script>
<![CDATA[
// The application code goes here
]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="Output">
<mx:TextArea id="taFiles" width="320" height="150"/>
</mx:FormItem>
</mx:Form>
</mx:WindowedApplication>
프로그램의 시작 부분에서는 다음 작업을 수행합니다.
-
필요한 클래스 가져오기
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
사용자 인터페이스 정의 정의
import fl.controls.*;
//requires TextArea and Label components in the Library
var taFiles = new TextArea();
var output = new Label();
taFiles.setSize(320, 150);
taFiles.move(10, 30);
output.move(10, 10);
output.width = 150;
output.text = "Contents of HelloAir.zip";
addChild(taFiles);
addChild(output);
-
bytes
ByteArray 정의
var bytes:ByteArray = new ByteArray();
-
파일 헤더의 메타데이터를 저장할 변수 정의
// variables for reading fixed portion of file header
var fileName:String = new String();
var flNameLength:uint;
var xfldLength:uint;
var offset:uint;
var compSize:uint;
var uncompSize:uint;
var compMethod:int;
var signature:int;
-
.zip 파일을 나타낼 File(
zfile
) 및 FileStream(
zStream
) 객체 정의 및 파일을 추출할 .zip 파일(데스크톱 디렉토리에서 "HelloAIR.zip"이라는 파일)의 위치 지정
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
Flex에서 프로그램 코드는
init()
메서드에서 시작하며 이 메서드는 루트
mx:WindowedApplication
태그에 대한
creationComplete
핸들러로 호출됩니다.
// for Flex
private function init():void
{
이 프로그램은 READ 모드로 .zip 파일을 열어 작업을 시작합니다.
zStream.open(zfile, FileMode.READ);
그런 다음
bytes
의
endian
속성을
LITTLE_ENDIAN
으로 설정하여 숫자 필드의 바이트 순서에서 최하위 바이트가 먼저 나옴을 나타냅니다.
bytes.endian = Endian.LITTLE_ENDIAN;
다음으로
while()
문은 파일 스트림의 현재 위치가 파일 크기보다 크거나 같을 때까지 계속되는 루프를 시작합니다.
while (zStream.position < zfile.size)
{
루프 내의 첫 번째 문은 파일 스트림의 첫 30바이트를 ByteArray
bytes
로 읽어 옵니다. 첫 30바이트는 첫 번째 파일 헤더의 고정 크기 부분을 구성합니다.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
다음으로 이 코드는 30바이트 헤더의 첫 번째 바이트에서 정수(
signature
)를 읽습니다. ZIP 형식 정의는 모든 파일 헤더의 서명이 16진수 값
0x04034b50
임을 지정합니다. 서명이 다른 경우 코드가 .zip 파일의 파일 부분 밖으로 이동했으며 더 이상 추출할 파일이 없음을 나타냅니다. 이 경우 코드는 바이트 배열의 끝을 기다리는 대신
while
루프를 즉시 종료합니다.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
코드의 다음 부분은 오프셋 위치 8에서 헤더 바이트를 읽고 해당 값을
compMethod
변수에 저장합니다. 이 바이트에는 이 파일을 압축하는 데 사용된 압축 방법을 나타내는 값이 포함됩니다. 여러 가지 압축 방법이 허용되지만 실제로는 거의 모든 .zip 파일이 DEFLATE 압축 알고리즘을 사용합니다. 현재 파일이 DEFLATE 압축으로 압축된 경우
compMethod
는 8이고 파일이 압축 해제된 경우
compMethod
는 0입니다.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
첫 30바이트 다음에는 파일 이름과 추가 필드가 포함될 수 있는 헤더의 가변 길이 부분이 옵니다.
offset
변수는 이 부분의 크기를 저장합니다. 크기는 오프셋 26 및 28의 헤더에서 읽은 파일 이름 길이 및 추가 필드 길이를 더하여 계산됩니다.
offset = 0; // stores length of variable portion of metadata
bytes.position = 26; // offset to file name length
flNameLength = bytes.readShort(); // store file name
offset += flNameLength; // add length of file name
bytes.position = 28; // offset to extra field length
xfldLength = bytes.readShort();
offset += xfldLength; // add length of extra field
다음으로 이 프로그램은
offset
변수에 저장된 바이트 수에 대해 파일 헤더의 가변 길이 부분을 읽습니다.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
이 프로그램은 헤더의 가변 길이 부분에서 파일 이름을 읽어 파일의 압축된 크기 및 압축 해제된(원본) 크기와 함께 텍스트 영역에 표시합니다.
// Flash version
bytes.position = 30;
fileName = bytes.readUTFBytes(flNameLength); // read file name
taFiles.appendText(fileName + "\n"); // write file name to text area
bytes.position = 18;
compSize = bytes.readUnsignedInt(); // store size of compressed portion
taFiles.appendText("\tCompressed size is: " + compSize + '\n');
bytes.position = 22; // offset to uncompressed size
uncompSize = bytes.readUnsignedInt(); // store uncompressed size
taFiles.appendText("\tUncompressed size is: " + uncompSize + '\n');
// Flex version
bytes.position = 30;
fileName = bytes.readUTFBytes(flNameLength); // read file name
taFiles.text += fileName + "\n"; // write file name to text area
bytes.position = 18;
compSize = bytes.readUnsignedInt(); // store size of compressed portion
taFiles.text += "\tCompressed size is: " + compSize + '\n';
bytes.position = 22; // offset to uncompressed size
uncompSize = bytes.readUnsignedInt(); // store uncompressed size
taFiles.text += "\tUncompressed size is: " + uncompSize + '\n';
이 예제에서는 파일의 나머지 부분을 파일 스트림에서 압축된 크기로 지정된 길이에 대한
bytes
로 읽어 와 첫 30바이트의 파일 헤더를 덮어씁니다. 파일이 압축되지 않은 경우에도 압축된 크기는 정확합니다. 이는 압축된 크기가 파일의 압축 해제된 크기와 같기 때문입니다.
// read compressed file to offset 0 of bytes; for uncompressed files
// the compressed and uncompressed size is the same
if (compSize == 0) continue;
zStream.readBytes(bytes, 0, compSize);
다음으로 이 예제에서는 압축된 파일을 압축 해제하고
outfile()
함수를 호출하여 출력 파일 스트림에 씁니다. 이 예제에서는
outfile()
에 파일 이름과 파일 데이터가 포함된 바이트 배열을 전달합니다.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
앞서 언급한 예제에서
bytes.uncompress(CompressionAlgorithm.DEFLATE)
는 AIR 응용 프로그램에서만 작동합니다. AIR 및 Flash Player에 대해 Deflate 방식의 데이터 압축을 수행하려면 ByteArray의
inflate()
함수를 호출해야 합니다.
닫는 괄호는
outFile()
메서드를 제외하고
while
루프,
init()
메서드 및 Flex 응용 프로그램 코드의 끝을 나타냅니다.
while
루프의 시작 부분으로 실행 작업이 다시 돌아가며 다른 파일을 추출하거나 마지막 파일이 처리된 경우 .zip 파일 처리를 끝내 .zip 파일의 다음 바이트 처리를 계속합니다.
} // end of while loop
} // for Flex version, end of init() method and application
outfile()
함수는 데스크톱에서 WRITE 모드로 출력 파일을 열고
filename
매개 변수를 통해 제공된 이름을 해당 파일에 지정합니다. 그런 다음 이 함수는
data
매개 변수에서 출력 파일 스트림(
outStream
)으로 파일 데이터를 쓰고 파일을 닫습니다.
// Flash version
function outFile(fileName:String, data:ByteArray):void
{
var outFile:File = File.desktopDirectory; // destination folder is desktop
outFile = outFile.resolvePath(fileName); // name of file to write
var outStream:FileStream = new FileStream();
// open output file stream in WRITE mode
outStream.open(outFile, FileMode.WRITE);
// write out the file
outStream.writeBytes(data, 0, data.length);
// close it
outStream.close();
}
private function outFile(fileName:String, data:ByteArray):void
{
var outFile:File = File.desktopDirectory; // dest folder is desktop
outFile = outFile.resolvePath(fileName); // name of file to write
var outStream:FileStream = new FileStream();
// open output file stream in WRITE mode
outStream.open(outFile, FileMode.WRITE);
// write out the file
outStream.writeBytes(data, 0, data.length);
// close it
outStream.close();
}