3 pièce(s) jointe(s)
Patch probleme java.util.zip
Bonjour,
Le package java.util.zip génère une exception lors de la décompression de fichiers dont le nom comporte des caractères non UTF-8.
La résolution de ce bug est vivement souhaitée par les développeurs des 4 coins du monde, d'où sa présence à la 2ème place du top 25 des bugs JAVA:
http://bugs.sun.com/bugdatabase/top25_bugs.do
De plus, lors de la compression, le nom des entrées est mal converti pour les mêmes raisons.
Je propose de corriger ce bug ici et de l'envoyer à SUN pour l'inclure dans une prochaine release. (why not)
J'ai commencé à y jetter un coup d'oeil pour la décompression, et j'arrive à décompresser des fichiers ZIP avec des caractères accentués sans générer d'exception.
Vous trouverez l'ensemble des classes en PJ:
- Le ZipInputStream qui pose problème et qui a subit quelques modifs de ma part.
- ZipConstant n'a pas changé mais comme les constantes ne sont pas publiques, alors il a été nécessaire de le copier dans le package.
- ZipUtils, ma classe utilitaire ZIP, exploitable, mais pas dans sa version finale.
Utilisation: disposer de documents avec des accents ou autres caractères mal gérés dans le répertoire test, créés à la racine du projet ; les zipper avec Winzip (nom=fromWinzip.zip), Winrar (nom=fromWinrar.zip).
Exemple d'utilisation dans un petit test.java contenant un petit main:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void main(String[] args) {
String path = "test/";
try {
ZipUtils.compress(path, new File(path+"fromJAVAZIP.zip"));
ZipUtils.decompress(new File(path + "fromWinzip.zip"), path + "/unzippedWINZIP");
//ZipUtils.decompress(new File(path + "fromWInrar.zip"), path + "/unzippedWINRAR");
//ZipUtils.decompress(new File(path + "fromJAVAZIP.zip"), path + "/unzippedJAVAZIP");
} catch (Exception e) {
e.printStackTrace();
}
} |
Pour ceux qui sont vraiment experts, et qui peuvent se passer de ce genre de tests, voici là où le problème se pose:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| /**
* Fetches a UTF8-encoded String from the specified byte array.
*/
private static String getUTF8String(byte[] b, int off, int len) {
// First, count the number of characters in the sequence
int count = 0;
int max = off + len;
int i = off;
while (i < max) {
int c = b[i++] & 0xff;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
count++;
break;
case 12:
case 13:
// 110xxxxx 10xxxxxx
if ((int) (b[i++] & 0xc0) != 0x80) {
throw new IllegalArgumentException();
}
count++;
break;
case 14:
// 1110xxxx 10xxxxxx 10xxxxxx
if (((int) (b[i++] & 0xc0) != 0x80)
|| ((int) (b[i++] & 0xc0) != 0x80)) {
throw new IllegalArgumentException();
}
count++;
break;
default:
// 10xxxxxx, 1111xxxx
throw new IllegalArgumentException();
}
}
if (i != max) {
throw new IllegalArgumentException();
}
// Now decode the characters...
char[] cs = new char[count];
i = 0;
while (off < max) {
int c = b[off++] & 0xff;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
// 0xxxxxxx
cs[i++] = (char) c;
break;
case 12:
case 13:
// 110xxxxx 10xxxxxx
cs[i++] = (char) (((c & 0x1f) << 6) | (b[off++] & 0x3f));
break;
case 14:
// 1110xxxx 10xxxxxx 10xxxxxx
int t = (b[off++] & 0x3f) << 6;
cs[i++] = (char) (((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
break;
default:
// 10xxxxxx, 1111xxxx
throw new IllegalArgumentException();
}
}
return new String(cs, 0, count);
}
private String getZipEntryName(byte[] b, int off, int len) {
String result = null;
try {
result = new String(b, off, len, "UTF-8");
} catch (UnsupportedEncodingException e) {
result = new String(b, off, len);
}
return result;
} |
Dans la version originale, l'appel se fait avec getUTF8String et le plantage se situe
Code:
1 2 3
| default:
// 10xxxxxx, 1111xxxx
throw new IllegalArgumentException(); |
Ma solution a consisté pour le moment à bifurquer l'appel vers getZipEntryName.
Mais je bloque, d'où la demande de la contribution de personnes plus confirmées que moi.
Cordialement,
Billy
EDIT: SystemUtils est une autre classe non incluse en PJ
Code:
1 2
| FILE_SEPARATOR = System.getProperty("file.separator");
FILE_ENCODING = System.getProperty("file.encoding"); |