TypeScript zipファイルの読み込みと作成 JavaScript
JavaScriptでzipファイルが使えると何かと便利なので作ってみました。圧縮展開はAPIを利用、ソースコードはすべて白紙の状態から作成しています。オープンソースや外部のライブラリ未使用です。
zipファイルからファイル情報取得
文章で説明するよりソースコードで
文字コード変換や圧縮展開などの実装は以前の記事または、この後の有料記事にあります。
// pkzip constant
const EOCD_SIZE = 22;//ファイルコメントなしの最小サイズ
const SIG_EOCD = (0x50) +(0x4b<<8) + (0x05<<16) + (0x06<<24);
const SIG_CENTRAL = (0x50) +(0x4b<<8) + (0x01<<16) + (0x02<<24);
const SIG_LOCAL = (0x50) +(0x4b<<8) + (0x03<<16) + (0x04<<24);
const COMP_DEFLATE = 0x8;
const COMP_NONE = 0;
const LOCAL_HEADER_SIZE = 30;
interface FileReaderIF{
arrayBuffer(begin : number, end : number) : Promise<ArrayBuffer>;
size : number;
}
//---------------------------
// zip(pkzip)からファイル情報取得
export async function parsePkZip(reader : FileReaderIF){
let files : PkZipFileHeader[] = [];
let fileSize = reader.size;
if(fileSize < EOCD_SIZE){
throw "parsePkZip file size";
}
let eocd = await reader.arrayBuffer(fileSize-EOCD_SIZE, fileSize);
if(!eocd || eocd.byteLength == 0){
throw "parsePkZip eocd";
}
let readbin = new ReadBinary(new Uint8Array(eocd));
//ファイルコメントありZIP未対応 PK0506を検索する必要あり
if(readbin.getU32() != SIG_EOCD){
// PK 05 06
throw "parsePkZip eocd sig";
}
readbin.getU16();//disk num
readbin.getU16();//central disk no
readbin.getU16();//entry num in disk
readbin.getU16();// entryNum
// セントラル読み込み
let centralSize = readbin.getU32();
let centralPtr = readbin.getU32();
let centralData = await reader.arrayBuffer(centralPtr, centralPtr+centralSize);
if(!centralData || centralData.byteLength != centralSize){
throw "parsePkZip central";
}
let readc = new ReadBinary(new Uint8Array(centralData));
let texdec = new TextDecoderSJISorUTF8();
while(!readc.isTerm()){
if(readc.getU32() != SIG_CENTRAL){
throw "parsePkZip central sig";
}
let makeVer = readc.getU16();
let needVer = readc.getU16();
if(needVer > 20){
throw "parsePkZip version";
}
let bitFlag = readc.getU16();
if(bitFlag != 0){
throw "parsePkZip bitFlag!=0";
}
let compType = readc.getU16();
if(compType != COMP_DEFLATE && compType != COMP_NONE){
throw "parsePkZip comp";
}
let dostime = readc.getU32();//MSDOS date time
let dateTime = dateFromMSDOS(dostime);
let crc32 = readc.getU32();
let compSize = readc.getU32();
let fileSize = readc.getU32();
let fileNameLen = readc.getU16();
let extLen = readc.getU16();
let commentLen = readc.getU16();
readc.getU16(); // diskNo
readc.getU16(); // attrIn
readc.getU32(); // attrOut
let fileOffset = readc.getU32();
let nameBin = readc.getBin(fileNameLen);
let fileName = texdec.decode(nameBin);
readc.skip(extLen);
readc.skip(commentLen);
// ローカルファイルヘッダとの照合
let localheader = await reader.arrayBuffer(fileOffset, fileOffset+LOCAL_HEADER_SIZE);
if(!localheader || localheader.byteLength == 0){
throw "parsePkZip localheader";
}
let rblocal = new ReadBinary(new Uint8Array(localheader));
if(rblocal.getU32() != SIG_LOCAL){
throw "parsePkZip sig localheader";
}
rblocal.getU16();//ver
rblocal.getU16();//bitFlag
if(rblocal.getU16() != compType){
throw "PkZipFiles::parseFileInfo compType";
}
rblocal.getU32();//msdos time
if(rblocal.getU32() != crc32){ throw "parsePkZip crc32";}
if(rblocal.getU32() != compSize){ throw "parsePkZip compSize";}
if(rblocal.getU32() != fileSize){ throw "parsePkZip fileSize";}
let name_len = rblocal.getU16();
let ext_len = rblocal.getU16();
let filePtr = fileOffset + LOCAL_HEADER_SIZE + name_len + ext_len;
let header = new PkZipFileHeader(fileName, compType,compSize, fileSize, filePtr, crc32, dateTime);
files.push(header);
}
return files;
}
圧縮展開とzipファイル作成(有料)
実践的なソースコードです。
文字コードはSJISとUTF-8に対応(Windowsとそれ以外)
APIを利用した圧縮展開(CompressionStream DecompressionStream)
機能テスト用ソースコード
zipファイルを読み込んで全ファイル展開、それをまたzipに変換
let file = await (await fetch("./zip.zip")).arrayBuffer();
// ファイルアクセスIF
let reader : FileReaderIF= {
arrayBuffer : async (b,e)=>{
return file.slice(b,e);
},
size : file.byteLength
};
// zipからファイル情報取得
let files = await parsePkZip(reader);
console.log(files);
// ファイル展開 DecompressionStreamを使用
let zipfiles : [string, Date, ArrayBuffer][] = [];
for(let f of files){
let data = await decompressPkZipFile(f , reader);
zipfiles.push([f.fileName, f.date, data]);
}
// zip作成 圧縮にはCompressionStream
// Windowsなら"sjis" それ以外は"utf-8"
let zip = await createPkZip(zipfiles, "sjis");
// zipファイルダウンロード
let a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([zip]));
a.download = "zip.zip";
document.body.append(a);
a.textContent = "download"
クラス定義 関数定義
記事購入の際の参考にしてください。
// zip(pkzip)からファイル情報取得
export async function parsePkZip(reader : FileReaderIF);// PkZipFileHeader[]
// zip(pkzip)作成
export async function createPkZip(zipfiles : [string, Date, ArrayBuffer][], encode : "sjis"|"utf-8" = "sjis");
// zipファイルの展開
export async function decompressPkZipFile(header : PkZipFileHeader, reader : FileReaderIF);
// deflate展開
export async function decompressPkZipDeflate(blob : Blob);
// deflate圧縮
export async function compressPkZipDeflate(blob : Blob);
// Utility
// バイナリー読み込み <- Uint8array
export class ReadBinary{}
// バイナリー書き込み -> Uint8array
export class WriteBinary{}
// MSDOS(FAT) -> JavaScript Data
function dateFromMSDOS(d : number);
// JavaScript Data -> MSDOS(FAT)
function msdosFromDate(date : Date) : number;
// SJIS or UTF-8 -> string
export class TextDecoderSJISorUTF8{}
// 文字コード変換 String(UTF-16) -> SJIS
// 簡易版? プログラム実験用 サロゲートペア未対応
export class TextEncoderSJIS{}
// crc32
class CRC32{}
ソースコード
ここから先は
16,244字
¥ 600
この記事が役に立ったという方は、サポートお願いします。今後の製作の励みになります。