5

है, मैं उत्पादन में देख रहे किनारे के मामले के साथ प्रयोग कर रहा हूं। हमारे पास एक व्यावसायिक मॉडल है जहां ग्राहक टेक्स्ट फाइलें उत्पन्न करते हैं और फिर उन्हें हमारे सर्वर पर एफ़टीपी करते हैं। हम उन फ़ाइलों को निगलना और उन्हें हमारे जावा बैकएंड पर संसाधित करते हैं (CentOS मशीनों पर चल रहे हैं)। हमारे ग्राहकों के अधिकांश (9 5% +) यूटीएफ -8 में इन फ़ाइलों को उत्पन्न करने के बारे में जानते हैं जो हम चाहते हैं। हालांकि हमारे पास कुछ जिद्दी ग्राहक हैं (लेकिन बड़े खाते) जो इन फ़ाइलों को सीपी 1252 चरित्र सेट के साथ विंडोज मशीन पर उत्पन्न करते हैं। हालांकि, कोई समस्या नहीं है, हमने कुछ तृतीय पक्ष libs (जो हमारे लिए "प्रसंस्करण" का अधिकांश काम करते हैं) को कुछ जादुई वू डू के माध्यम से किसी भी चरित्र सेट में इनपुट को संभालने के लिए कॉन्फ़िगर किया है।जावा फ़ाइल सिस्टम पर फ़ाइल नहीं देख सकता है जिसमें अवैध वर्ण

कभी-कभी, हम एक फ़ाइल देखते हैं जिसमें अवैध नाम यूटीएफ -8 वर्ण (सीपी 1252) हैं। हमारे सॉफ्टवेयर FTP सर्वर फ़ाइल पढ़ने chokes के सामान्य विधि से में इन फ़ाइलों को पढ़ने के लिए कोशिश करता है और फेंकता है जब एक FileNotFoundException:

File f = getFileFromFTPServer(); 
FileReader fReader = new FileReader(f); 

String line = fReader.readLine(); 
// ...etc. 

अपवाद कुछ इस तरह दिखाई:

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) 

तो मैं क्या सोचते हैं ऐसा हो रहा है क्योंकि फ़ाइल नाम में अवैध वर्ण हैं, हम इसे पहले स्थान पर भी कभी नहीं पढ़ पाएंगे। यदि हम कर सकते हैं, तो फ़ाइल की सामग्री के बावजूद, हमारा सॉफ़्टवेयर इसे सही तरीके से संसाधित करने में सक्षम होना चाहिए। तो यह वास्तव में अवैध यूटीएफ -8 वर्णों के साथ फ़ाइल नाम पढ़ने के साथ एक मुद्दा है।

एक परीक्षण मामले के रूप में, मैंने अपने सर्वरों में से एक पर तैनाती के लिए एक बहुत ही सरल जावा "ऐप" बनाया और कुछ चीजों का परीक्षण (स्रोत कोड नीचे दिया गया है) का परीक्षण किया। मैंने फिर एक विंडोज मशीन में लॉग इन किया और एक टेस्ट फाइल बनाई और इसे test£.txt नाम दिया। फ़ाइल नाम में "परीक्षण" के बाद चरित्र पर ध्यान दें। यह Alt-0163 है। मैंने इसे हमारे सर्वर पर एफ़टीपी किया, और जब मैंने अपनी मूल निर्देशिका पर ls -ltr चलाया, तो मुझे यह देखने में आश्चर्य हुआ कि यह test?.txt के रूप में सूचीबद्ध है।

इससे पहले कि मैं किसी भी आगे जाना है, यहाँ जावा "एप्लिकेशन" मैं परीक्षण के लिए लिखा है/इस मुद्दे के पुनरुत्पादन है: जब मैं टर्मिनल (java -cp . com/Driver t*) से चलाने

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() + ")"); 
      } 
     } 
    } 
} 

, मैं निम्नलिखित उत्पादन प्राप्त करें:

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?!? मैंने कुछ शोध किया और पाया कि "�" यूनिकोड प्रतिस्थापन चरित्र \uFFFD है। तो मैं अनुमान क्या हो रहा है यह है कि CentOS FTP सर्वर को पता नहीं है कि Alt-0163 (£) को कैसे प्रबंधित किया जाए और इसलिए यह \uFFFD (�) के साथ बदल देता है। लेकिन मैं क्यों समझ में नहीं आता ls -ltr प्रदर्शित करता है एक फ़ाइल test?.txt कहा जाता है ...

जो भी हो, ऐसा लगता है कि समाधान कुछ तर्क यह है कि फ़ाइल के नाम में इस चरित्र के अस्तित्व की खोज करता है जोड़ने के लिए है, और अगर पाया , किसी अन्य चीज़ को फ़ाइल का नाम बदलता है (जैसे शायद स्ट्रिंग-वार replaceAll("\uFFFD", "_") या ऐसा कुछ करें) कि सिस्टम पढ़ और संसाधित कर सकता है।

समस्या यह है कि जावा फ़ाइल सिस्टम पर यह फ़ाइल नहीं देखता है। CentOS जानता है कि फ़ाइल वहां है (test?.txt), लेकिन जब वह फ़ाइल जावा में पारित हो जाती है, तो जावा इसे test�.txt के रूप में व्याख्या करता है और किसी कारण से No such file or directory ...

मैं इस फ़ाइल को देखने के लिए जावा कैसे प्राप्त कर सकता हूं ताकि मैं File::renameTo(String) पर प्रदर्शन कर सकूं? यहां बैकस्टोरी के लिए खेद है, लेकिन मुझे लगता है कि यह प्रासंगिक है क्योंकि इस परिदृश्य में प्रत्येक विवरण की गणना की जाती है। अग्रिम में धन्यवाद!

+0

ताकि आप निर्देशिका में फ़ाइलों को सूचीबद्ध नहीं कर सकें, फिर देखें कि उनके नाम में "विषम अक्षर" हैं और उन्हें "timestamp + random.something" में फ़ाइल.इनैम के साथ नाम दें? –

+0

@ मार्कस मिककोलेन - क्या आप इसे मैन्युअल रूप से करने के बारे में बात कर रहे हैं? यदि नहीं, तो आप किस भाषा/स्क्रिप्ट का जिक्र कर रहे हैं? – IAmYourFaja

+0

मेरा सुझाव है कि आप फाइलनामों को पास करने के बजाय फ़ाइल ऑब्जेक्ट्स का उपयोग करें। जो शायद किसी भी फाइलनाम भ्रष्टाचार को रोक देगा। –

उत्तर

5

टेक्स्ट एन्कोडिंग की अद्भुत दुनिया में आपका स्वागत है। आपके पास कई स्तर की समस्याएं हैं और आपको उनमें से प्रत्येक को व्यक्तिगत रूप से सॉर्ट करने की आवश्यकता है।

सबसे पहले, डिस्क पर फ़ाइल नाम क्या है? क्या इसमें वैध यूटीएफ -8 भागने के दृश्य हैं या क्या यह कुछ और है?

समस्या यह है कि आपको सही फ़ाइल नाम या Windows फ़ाइल सिस्टम की आवश्यकता है, बस फ़ाइल को ढूँढने में सक्षम नहीं होगा। इसके शीर्ष पर, विंडोज़ अवैध नामों को फ़ाइल नाम में यूनिकोड \uFFFD में परिवर्तित करने का प्रयास कर सकता है, चाहे आप जो भी प्रयास करें, आप फ़ाइल लोड नहीं कर पाएंगे (क्योंकि डिस्क पर \uFFFD के साथ कोई फ़ाइल नहीं है)।

यह कैसे हो सकता है? ऐसा इसलिए होता है क्योंकि मैपिंग दो-तरफा नहीं है। जब विंडोज डिस्क से फ़ाइल नाम लोड करता है, तो यह को test\uFFFD.txt के साथ बदल देता है और आपको वह नाम देता है। जब आप test\uFFFD.txt खोलने के लिए विंडोज़ को बताते हैं, तो यह फ़ाइल नहीं ढूंढ पाएगा क्योंकि इस तरह के नाम के साथ कोई फ़ाइल नहीं है (केवल test�.txt है)। आपके लिए यह पता लगाने का कोई तरीका नहीं है कि फ़ाइल का वास्तविक नाम क्या है।

समाधान? आप एक डॉस प्रॉम्प्ट खोल सकते हैं और ren test*.txt test.txt पैटर्न के साथ फ़ाइल का नाम बदल सकते हैं। चूंकि पैटर्न केवल एक फ़ाइल से मेल खाता है, यह काम करेगा। लेकिन आप विंडोज एक्सप्लोरर से ऐसा करने में सक्षम नहीं होंगे क्योंकि यह फ़ाइल भी नहीं ढूंढ सकता है।

अगला चरण: एफ़टीपी। एफ़टीपी मनुष्यों के लिए एक प्रोटोकॉल है - यह स्वचालित डेटा एक्सचेंज के लिए उपयुक्त नहीं है। एफ़टीपी से छुटकारा पाएं। मुझे नहीं पता कि इससे आपको कितना खर्च आएगा लेकिन यह हमेशा इसके लायक है। एसएफटीपी, एसपीपी या FTAPI का प्रयोग करें।

समस्या का एक स्रोत यह हो सकता है कि एफ़टीपी फ़ाइल नाम ASCII के रूप में स्थानांतरित कर दे। एफ़टीपी प्रोटोकॉल में कोई उमॉट्स की अनुमति नहीं है ... या इसके बजाय, एफ़टीपी किसी की अपेक्षा नहीं करता है। यदि आप भाग्यशाली हैं, तो आपका एफ़टीपी क्लाइंट फ़ाइल को स्थानांतरित करने से इंकार कर देगा लेकिन सबसे अधिक आसानी से बग आउट हो जाएगा। लेकिन जब वे मौजूद होते हैं, तो एफ़टीपी बस कुछ करेगा ... कुछ। जो कुछ भी हो सकता है। यहां सामान्य प्रभाव यह है कि नाम में यूनिकोड वाली फाइलें यूटीएफ -8 के रूप में दो बार एन्कोड की गई हैं या यूनिकोड को ? (\u003f) के साथ प्रतिस्थापित किया गया है।

या जावा एफ़टीपी क्लाइंट new String(bytes) का उपयोग एफ़टीपी फ़ाइल नाम से स्ट्रिंग बनाने के लिए कर सकता है जो आपके सिस्टम के डिफ़ॉल्ट एन्कोडिंग के साथ खराब बाइट्स को बलात्कार करेगा - सुंदर नहीं।

समाधान:

  1. FTP सर्वर जो उनके नाम में अवैध रूप से पात्रों के साथ फ़ाइलों को खारिज कर दिया प्रयोग करें या जो कुछ है कि फाइल सिस्टम/ओएस को भ्रमित नहीं है करने के लिए इन पात्रों बदल देता है।
  2. एक फ़ाइल सिस्टम का उपयोग करें जो अजीब नामों वाली फ़ाइलों को सही तरीके से संभालता है। आमतौर पर सर्वर पर विंडोज से छुटकारा पाने का मतलब है।
  3. सुनिश्चित करें कि उपयोगकर्ता केवल एक ही निर्देशिका में अपलोड कर सकते हैं और यह निर्देशिका में केवल एक फ़ाइल हो सकती है। इस तरह, आप इसे पढ़ने के लिए किसी छोटे से खोल स्क्रिप्ट और पैटर्न का उपयोग कर सकते हैं जिसे आप पढ़ सकते हैं।
1

यह पुराने-स्कूल जावा फ़ाइल एपीआई में एक बग है, शायद मैक पर? वैसे भी, नया java.nio api बहुत बेहतर काम करता है।मेरे पास कई फाइलें हैं जिनमें यूनिकोड वर्ण हैं जो java.io ... वर्गों का उपयोग करके लोड करने में विफल रहे हैं। java.nio.Path का उपयोग करने के लिए मेरे सभी कोड को कनवर्ट करने के बाद सब कुछ काम करना शुरू कर दिया। और मैं अपाचे FileUtils (जो एक ही समस्या है) को बदल दिया java.nio.Files साथ ...

पढ़ सकते हैं और एक उपयुक्त चारसेट का उपयोग कर फ़ाइल की सामग्री, उदाहरण के लिए लिखना न भूलें: Files.readAllLines (myPath, StandardCharsets.UTF_8)