超久しぶりに zip.js を使ったりしたのですが、
なんか問題が起きたりしました(^_^;)
調べてみた所、
ファイル名のエンコードが UTF8 の場合に起こるようです。
どうもオリジナルのコードに問題がありそうな雰囲気?
というか対応が不十分?
ところで、
ZIP内部のファイル名エンコーディングは、
ASCII か UTF8 のいずれかになります。
一方、日本語版Windowsにおける既定の文字エンコーディングはSJIS。
Microsoft的にはCP932とか言ったりするようです。
そんなわけで、マルチバイトなSJISのファイル名は、
ZIP内部ではASCIIとして扱われるのが通常?のようです。
ただ、場合によっては変換して UTF8 として扱うことがあるようです。
どちらになるのかの条件はよく分かってません。
使用したソフトによっても異なるかもしれません。
ともかくも改善することにしました。
また、ASCIIとして扱われたマルチバイトな文字を
デコードするためにカスタマイズできるようにもしました。
ということで、
"zip.js"
の一部を以下のように改造しました。
function decodeASCII( bytes ) { // *** MOD ***
var i, il, out = '', charCode, extendedASCII = [ '\u00C7', '\u00FC', '\u00E9',
.
.
.
];
for (i = 0, il = bytes.length; i < il; i++) {
charCode = bytes[i];
out += charCode >= 128 ? extendedASCII[charCode - 128] : String.fromCharCode( charCode );
}
return out;
}
function decodeUTF8( bytes ) { // *** MOD ***
// cf. https://ja.wikipedia.org/wiki/UTF-8
// cf. https://qiita.com/YusukeHirao/items/2f0fb8d5bbb981101be0
var c, s = '', i = 0, l = bytes.length;
while ( l > 0 ) {
c = bytes[i++];
if ( (c & 0x80) === 0 ) {
// 1 byte (7 bit)
s += String.fromCharCode( c );
l--;
} else
if ( ( c & 0xe0 ) === 0xc0 ) {
// 2 bytes (11 bit)
c &= 0x1f;
c = ( c << 6 ) | ( bytes[i++] & 0x3f );
s += String.fromCharCode( c );
l -= 2;
} else
if ( ( c & 0xf0 ) === 0xe0 ) {
// 3 bytes (16 bit)
c &= 0x0f;
c = ( c << 6 ) | ( bytes[i++] & 0x3f );
c = ( c << 6 ) | ( bytes[i++] & 0x3f );
s += String.fromCharCode( c );
l -= 3;
} else
if ( ( c & 0xf8 ) === 0xf0 ) {
// 4 bytes (21 bit)
c &= 0x07;
c = ( c << 6 ) | ( bytes[i++] & 0x3f );
c = ( c << 6 ) | ( bytes[i++] & 0x3f );
c = ( c << 6 ) | ( bytes[i++] & 0x3f );
c -= 0x10000;
s += String.fromCharCode( 0xd800 | (c >>> 10), 0xdc00 | (c & 0x3ff) ); // surrogate pair
l -= 4;
} else {
// ignore
l--;
}
}
return s;
}
function getString( utf8, bytes ) { // *** MOD ***
if ( utf8 ) {
return decodeUTF8( bytes );
} else {
if ( obj.zip.decodeString ) {
return obj.zip.decodeString( bytes );
} else {
return decodeASCII( bytes );
}
}
}
.
.
.
var zipReader = {
getEntries : function(callback) {
var worker = this._worker;
.
.
.
reader.readUint8Array(datalength, reader.size - datalength, function(bytes) {
var i, index = 0, entries = [], entry, data = getDataHelper(bytes.length, bytes); // *** MOD ***
for (i = 0; i < fileslength; i++) {
.
.
.
// *** MOD ***
//filename = getString(data.array.subarray(index + 46, index + 46 + entry.filenameLength));
//entry.filename = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(filename) : decodeASCII(filename);
entry.filename = getString((entry.bitFlag & 0x0800) === 0x0800, data.array.subarray(index + 46, index + 46 + entry.filenameLength));
if (!entry.directory && entry.filename.charAt(entry.filename.length - 1) == "/")
entry.directory = true;
// *** MOD ***
//comment = getString(data.array.subarray(index + 46 + entry.filenameLength + entry.extraFieldLength, index + 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength));
//entry.comment = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(comment) : decodeASCII(comment);
entry.comment = getString((entry.bitFlag & 0x0800) === 0x0800, data.array.subarray(index + 46 + entry.filenameLength + entry.extraFieldLength, index + 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength));
.
.
.
}
.
.
.
},
.
.
.
obj.zip = {
Reader : Reader,
Writer : Writer,
.
.
.
workerScripts : null,
// *** ADD ***
decodeString : null // function decodeString( bytes )
};
なお、
例えば encoding.js を使って decodeString
する際は
以下のようにしてください。
AUTO ではなく明示的に SJIS にしても構わないかもしれません。
bytes
は Uint8Array
な配列です。
zip.decodeString = function( bytes ) {
return Encoding.convert( bytes, { to:'UNICODE', from:'AUTO', type:'string' } );
// あるいは明示的に return Encoding.convert( bytes, { to:'UNICODE', from:'SJIS', type:'string' } );
};