2015-11-30 105 views
16

該代碼在大多數情況下都能正常工作,但有時會拋出異常。無法弄清楚是什麼原因造成的。爲什麼有時會拋出FileNotFoundException

什麼是確實是在

/storage/emulated/0/Download/theFileName.jpg

創建文件和寫入數據到它(從它的資源文件確實存在),但新創建的文件得到「文件不存在」異常。

(它確實在明顯有uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE", and uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE「)

File sourceFile = new File(theSourceFileFullPath); 
if (sourceFile.exists()) { 
    File downloadDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); 
    String downloadPath = downloadDirectory.getPath(); 
    String newFilePath = (downloadPath + "/" + fileName); 
    File newFile = new File(newFilePath); 
    try { 
     FileInputStream in = new FileInputStream(sourceFile); 

     // ava.io.FileNotFoundException: 
     //  /storage/emulated/0/Download/theFileName.jpg: open failed: ENOENT (No such file or directory) 
     // exception at this line  
     FileOutputStream out = new FileOutputStream(newFile); 
     //...... 
    } catch (Exception e) {} 
} 
+1

也許沒有完成它的創建,你嘗試訪問它。每次應用程序打開時,它都會嘗試創建一個文件並從中讀取它? –

+0

嘗試替換:File newFile = new File(downlaodDirectory,fileName); – phongvan

+0

Lispas,該應用程序已運行。 mdtuyen,File newFile = new File(downlaodDirectory,fileName);沒有幫助,並且如果同時具有downlaodDirectory和fileName,它與新文件(downlaodDirectory.getPath()+「/」+ fileName)相同; – lannyf

回答

7

我能想到的這種行爲如下可能的原因:

  1. 新的文件不能被簡單地創建,因爲有SD卡上沒有可用空間下一次遇到此問題時,請嘗試通過文件管理器或設置 - >存儲來查看是否至少有一些可用空間。如果發生這種情況,我懷疑您可以對此做些什麼用戶的設備。
  2. SD卡由於某些操作系統故障或其物理上未連接而無法使用,因此無法創建文件。再次 - 除了顯示一些Toast錯誤信息外,您無能爲力。
  3. 如何生成文件路徑的fileName部分? Sometimes無法創建文件,因爲文件名包含不支持的(由此特定設備)字符。例如。我有HTC Desire X與Android 4.1.2,如果文件名包含冒號,則無法創建類似於您的代碼的文件。嘗試另一種方式來生成文件名,看看它是否有所作爲。
  4. 在創建文件之前,您是否確定Downloads目錄存在?寫類似如下:

    if (!downloadDirectory.exists()) { 
        downloadDirectory.mkdirs(); 
    } 
    
  5. WRITE_EXTERNAL_STORAGE許可被視爲dangerous對Android 6,因此可以在任何時間,用戶可以撤銷。 Make sure用戶在寫入文件之前未取消該權限。
  6. Always close the I/O stream當你完成它的工作。在finally條款添加到您的try-catch塊,並在其關閉兩個inout流:

    finally { 
        if (in != null) { 
         try { 
          in.close(); 
         } catch (Exception e) { } 
        } 
    
        if (out != null) { 
         try { 
          out.close(); 
         } catch (Exception e) { } 
        } 
    } 
    
  7. 最後一個 - 最後我出的選項。 :)我想這個問題也可能出現,如果你從多個線程寫入相同的文件。如果是這種情況,請嘗試同步對文件寫入代碼的訪問。
+0

感謝aga!崩潰報告來自crashlytics,並且實例顯示該設備似乎不缺乏記憶。這可能是一個小故障,但有數百個報告。文件名很簡單,只要一個字。它不喜歡系統下載目錄不在那裏,如果它是那麼同一個用戶將有多個實例報告,但事實並非如此。用戶可以撤銷write_external_storage,但是實例分發從所有不同的設備交叉。再次感謝您列出可能的情況。 – lannyf

+0

@lannyf我忘記了另一個機會:當你完成與他們一起工作時,你沒有關閉'in'和'out'流。也試試這個。 – aga

+0

謝謝你。它確實在流上關閉,但沒有顯示在代碼片段中。 – lannyf

2

根據Java文檔 - Class FileNotFoundException:當試圖打開指定路徑名錶示的文件失敗

public class FileNotFoundException extends IOException

信號。

此異常將由FileInputStream被拋出, FileOutputStream,當與 文件指定pathname不存在RandomAccessFile構造。如果文件確實存在,但是由於某種原因, 將無法​​訪問,例如當試圖打開只讀 文件進行寫入時,它也會由這些構造函數拋出。

要恢復,有三種情況可能會引發FileNotFoundException

  1. 指定的文件不存在。
  2. 指定的文件實際上是一個目錄。
  3. 指定的文件由於某種原因無法打開以供閱讀。

新創建的文件得到「文件不存在」異常。

在你的代碼,我沒有看到兩件事情

  1. 成功寫入後刪除文件。我根本不是在開玩笑。如果我有這樣一個類和那個錯誤的類,我會做一種測試,從源文件複製內容後刪除downlaodDirectory。在這種情況下,它會通過該方法,或者你會得到另一個,但更具體的錯誤 - 就像我的話:無法刪除文件。正在使用它
  2. 寫入數據後關閉文件。我也沒有看到關閉文件的方法。在你的代碼中,似乎是你的文件仍然是打開的,所以你再次運行一個應用程序,你得到這個不可預知的錯誤。

要解決該問題,只需在使用file.close()方法處理數據後關閉這兩個文件。

希望它能幫助

+0

謝謝piotrek。該流確實關閉,它只是不顯示在代碼片段中。 #1,名稱文件不存在,需要在這裏創建。 #2,它是一個文件名而不是目錄。 #3這就是試圖找出原因和解決辦法的原因。 – lannyf

0
  1. 問題可能發生因創建文件的方式:

    String downloadPath = downloadDirectory.getPath(); 
    String newFilePath = (downloadPath + "/" + fileName); 
    File newFile = new File(newFilePath); 
    

    危險的代碼是在這裏:

    String newFilePath = (downloadPath + "/" + fileName); 
    

    相反,嘗試使用以下方法:

    File newFile = new File(downloadDirectory, fileName); 
    
  2. SD卡可用性 - 請檢查以下文檔 http://developer.android.com/training/basics/data-storage/files.html#WriteExternalStorage。 這可能是媒體不安裝(例如在一些舊設備 當用戶通過USB端口連接電腦連接設備碰巧)所以,儘量 檢查,如果媒體安裝:

    /* Checks if external storage is available for read and write */ 
    public boolean isExternalStorageWritable() { 
        String state = Environment.getExternalStorageState(); 
        if (Environment.MEDIA_MOUNTED.equals(state)) { 
         return true; 
        } 
        return false; 
    } 
    

    ,並通知用戶如果不。

+0

謝謝格雷戈裏!如果newFilePath =(downloadPath +「/」+ fileName);是問題,那麼它會有更多的例外。除了這兩個版本最終都會調用fixSlashes()。 aga已經提到了外部存儲的可用性,不要認爲這是設備「下載」文件夾的問題,如果問題是同一個用戶/設備會有更多的異常,情況並非如此。 – lannyf

+0

@lannyf MEDIA_MOUNTED問題有點棘手。它只發生在極少數情況下。一種可能的情況是,當用戶的設備具有SD卡並且用戶通過USB將設備(使用舊的Android)連接到計算機並選擇USB連接以傳輸文件時,可以對其進行測試。 (我的應用程序中有類似的問題,因爲我沒有SD卡,所以從未發生過這種問題。)「isExternalStorageWritable」由develper.android.com推薦,它很容易實現,所以我會研究它。 – GregoryK

+0

感謝Gregory!它獲得了很多報告的實例(對於相同的例外問題)在設備上發生沒有SD卡。 – lannyf

0

我希望你的代碼在文件中寫入數據是完美的我只是集中在你的問題。我認爲有兩種可能性得到文件沒有發現異常。

  1. 當你提供這種性格的當時的文件給出文件名不正確生成,這就是爲什麼這個錯誤是發生所以請確保您的文件名是正確的一段時間,文件名是不支持某些特殊字符。
  2. 秒是你的文件擴展名。在這裏你沒有指定你擁有哪種類型的文件。

這裏我有一些代碼片段我希望這會幫助你解決問題。

解決方案

File file=new File(Environment.getExternalStorageDirectory()+"/"+ConstansClass.FOLDERNAME); 
    if(!file.exists()) 
    { 
     file.mkdir(); 
    } 


    String file=filename.replaceAll("[^a-zA-Z0-9.-]", " "); 
    yourDir = new File(file, file+".mp3"); 
    if(yourDir.exists()) 
    { 
     return yourDir; 
    } 

編輯

對不起lannf我不知道是什麼在你的代碼究竟會發生,但我有一個代碼片斷這是剛剛從soundcloude下載MP3文件並存儲在我的本地storage.i認爲這是完全相同的您的要求。所以我有我的代碼下面。

@Override 
protected File doInBackground(Void... params) { 


    //File sdCardRoot = Environment.getExternalStorageDirectory()+"/Music"; 

    File file=new File(Environment.getExternalStorageDirectory()+"/"+ConstansClass.FOLDERNAME); 
    if(!file.exists()) 
    { 
     file.mkdir(); 
    } 


    String filename=soundCloudeResponse.getTitle();//.replaceAll("[^a-zA-Z0-9.-]", " "); 
    yourDir = new File(file, filename+".mp3"); 
    if(yourDir.exists()) 
    { 
     return yourDir; 
    } 
    String url=soundCloudeResponse.getStream_url()+"?"+ConstansClass.SOUNDCLOUDE_CLIENT_ID; 

    URL u = null; 
    try { 
     DebugLog.e("Request Url"+ url); 
     u = new URL(url); 
     URLConnection conn = u.openConnection(); 
     int contentLength = conn.getContentLength(); 

     DataInputStream stream = new DataInputStream(u.openStream()); 

     byte[] buffer = new byte[contentLength]; 
     stream.readFully(buffer); 
     stream.close(); 

     DataOutputStream fos = new DataOutputStream(new FileOutputStream(yourDir)); 
     fos.write(buffer); 
     fos.flush(); 
     fos.close(); 
     DebugLog.d("Download Complete in On Background"); 


    } catch (MalformedURLException e) { 
     sucess=false; 
     e.printStackTrace(); 

    } catch (IOException e) { 
     sucess=false; 
     e.printStackTrace(); 

    } 
    catch (Exception e) 
    { 
     sucess=false; 
     e.printStackTrace(); 
     DebugLog.e("Error ::"+e.getMessage()); 
    } 


    return yourDir; 
} 

我認爲你是聰明的開發者,所以你可以根據你的需求修改這段代碼。

最好的運氣

+0

謝謝Chirag! '/storage/emulated/0/Download/theFileName.jpg'是報告的異常之一的文件路徑。文件名稱正確與分機。 – lannyf

+0

@ lannf.Check編輯。 –

0

你沒貼全異常堆棧痕跡,但我發現確切的問題這麼解決了。

java.io.FileNotFoundException: /storage/emulated/0/bimagechooser/1429031333305.jpg: open failed: ENOENT (No such file or directory) 

使用這個庫image-chooser-library. &需要重新因子代碼。

這個傢伙coomar2841。一定會幫助你。因爲他花了數小時解決問題。所以節省你的時間。

接收com.kbeanie.imagechooser的源代碼,並將它們放到您的APP中,刪除該庫的gradle聲明並且APP應該可以工作。

請查看​​

我希望它會爲你工作。 :)

+0

感謝LukyBoy -KU! FileOutputStream out = new FileOutputStream(newFile);如片段中所指出的那樣。 – lannyf

+0

是具體的問題設備?可能是你可以通過GITHUB打開這個問題 –

+0

它不是特定於設備的,它已經在不同的設備上報告過,但顯然它也在大多數時間都有效。 – lannyf

相關問題