Sto sperimentando un caso limite che stiamo vedendo in produzione. Abbiamo un modello di business in cui i clienti generano file di testo e poi li FTP ai nostri server. Noi ingeriamo questi file e li elaboriamo sul nostro back-end Java (in esecuzione su macchine CentOS). La maggior parte (95% +) dei nostri clienti sa di generare questi file in UTF-8 che è ciò che vogliamo. Tuttavia abbiamo alcuni client testardi (ma grandi account) che generano questi file sulla macchina Windows con il set di caratteri CP1252. Nessun problema, abbiamo configurato le nostre librerie di terze parti (che sono ciò che fa per noi la maggior parte delle "elaborazioni") per gestire l'input in qualsiasi set di caratteri attraverso qualche magico voo doo.Java non può vedere il file sul file system che contiene caratteri non validi
Occasionalmente, viene visualizzato un file che contiene caratteri UTF-8 non validi (CP1252) nel suo nome. Quando il nostro software cerca di leggere questi file in dal server FTP il normale metodo di file di induttanze di lettura e getta una FileNotFoundException
:
File f = getFileFromFTPServer();
FileReader fReader = new FileReader(f);
String line = fReader.readLine();
// ...etc.
Le eccezioni simile a questa:
java.io.FileNotFoundException: /path/to/file/some-text-blah?blah.xml (No such file or directory) at java.io.FileInputStream.open(Native Method) at
java.io.FileInputStream.(FileInputStream.java:120) at java.io.FileReader.(FileReader.java:55) at com.myorg.backend.app.InputFileProcessor.run(InputFileProcessor.java:60) at
java.lang.Thread.run(Thread.java:662)
Quindi quello che penso sta succedendo è che poiché il file nome contiene caratteri illegali, non siamo nemmeno in grado di leggerlo in primo luogo. Se potessimo, quindi indipendentemente dal contenuto del file, il nostro software dovrebbe essere in grado di gestirlo correttamente. Quindi questo è davvero un problema con la lettura di nomi di file con caratteri UTF-8 non validi.
Come test case, ho creato una "app" Java molto semplice da installare su uno dei nostri server e testare alcune cose (il codice sorgente è fornito di seguito). Ho quindi effettuato l'accesso a un computer Windows e ho creato un file di test e lo ho denominato test£.txt
. Notare il carattere dopo "test" nel nome del file. Questo è Alt-0163. L'ho trasmesso via FTP al nostro server e quando ho eseguito l'operazione ls -ltr
nella directory principale, sono stato sorpreso di vederlo elencato come test?.txt
.
Prima di andare avanti, ecco il Java "app" che ho scritto per il test/riprodurre questo problema:
public Driver {
public static void main(String[] args) {
Driver d = new Driver();
d.run(args[0]); // I know this is bad, but its fine for our purposes here
}
private void run(String fileName) {
InputStreamReader isr = null;
BufferedReader buffReader = null;
FileInputStream fis = null;
String firstLineOfFile = "default";
System.out.println("Processing " + fileName);
try {
System.out.println("Attempting UTF-8...");
fis = new FileInputStream(fileName);
isr = new InputStreamReader(fis, Charset.forName("UTF-8"));
buffReader = new BufferedReader(isr);
firstLineOfFile = buffReader.readLine();
System.out.println("UTF-8 worked and first line of file is : " + firstLineOfFile);
}
catch(IOException io1) {
// UTF-8 failed; try CP1252.
try {
System.out.println("UTF-8 failed. Attempting Windows-1252...(" + io1.getMessage() + ")");
fis = new FileInputStream(fileName);
// I've also tried variations "WINDOWS-1252", "Windows-1252", "CP1252", "Cp1252", "cp1252"
isr = new InputStreamReader(fis, Charset.forName("windows-1252"));
buffReader = new BufferedReader(isr);
firstLineOfFile = buffReader.readLine();
System.out.println("Windows-1252 worked and first line of file is : " + firstLineOfFile);
}
catch(IOException io2) {
// Both UTF-8 and CP1252 failed...
System.out.println("Both UTF-8 and Windows-1252 failed. Could not read file. (" + io2.getMessage() + ")");
}
}
}
}
Quando eseguo questo dal terminale (java -cp . com/Driver t*
), ottengo il seguente output:
Processing test�.txt
Attempting UTF-8...
UTF-8 failed. Attempting Windows-1252...(test�.txt (No such file or directory))
Both UTF-8 and Windows-1252 failed. Could not read file.(test�.txt (No such file or directory))
test�.txt
?!?! Ho fatto alcune ricerche e ho scoperto che "�" è il carattere di sostituzione Unicode \uFFFD
. Quindi io indovina quello che succede è che il server FTP CentOS non sa come gestire Alt-0163 (£
) e quindi lo sostituisce con \uFFFD
(�
). Ma non capisco perché ls -ltr
visualizza un file chiamato test?.txt
...
In ogni caso, sembra che la soluzione sia aggiungere una logica che cerca l'esistenza di questo carattere nel nome del file, e se trovato , rinomina il file in qualcos'altro (come forse fa un replaceAll("\uFFFD", "_")
String-wise o qualcosa del genere) che il sistema può leggere ed elaborare.
Il problema è che Java non è nemmeno vedere questo file sul file system. CentOS sa che il file è lì (test?.txt
), ma quando quel file viene passato in Java, Java lo interpreta come test�.txt
e per qualche motivo No such file or directory
...
Come posso ottenere Java per vedere questo file in modo che possa eseguire un File::renameTo(String)
su di esso? Mi dispiace per il passato qui, ma ritengo che sia rilevante dal momento che ogni dettaglio conta in questo scenario. Grazie in anticipo!
quindi non puoi elencare i file nella directory, quindi vedere quali sono i "caratteri dispari" nel loro nome e rinominarli in "timestamp + random.something" con file.renameTo? –
@MarkusMikkolainen - stai parlando di farlo manualmente? Se non a quale lingua/sceneggiatura ti stai riferendo? – IAmYourFaja
Suggerisco di utilizzare gli oggetti File invece di passare i nomi dei file. questo probabilmente impedirà qualsiasi corruzione del nome file. –