首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android 文件移動失敗且源文件消失?IO 操作避坑指南

     編輯:程式設計綜合問答

問題現場

開發者嘗試將相冊照片移動至百度搜索預設圖片目錄,代碼邏輯為:打開輸入流讀取源文件,輸出流寫入目標文件,成功後刪除源文件。運行時無異常拋出,但目標目錄無文件,源文件卻不見了。

代碼核心缺陷

File newfile = new File(newPath); // newPath 是目錄路徑
if (!newfile.exists()) { return false; }
OutputStream opStream = new FileOutputStream(newfile); // 錯誤:對目錄創建輸出流

newFile 指向的是目標目錄而非目標文件,導致 `FileOutputStream` 拋出 `FileNotFoundException`(被 catch 吞噬),隨後 `oldfile.delete()` 執行,源文件被刪除,數據實際未寫入任何文件。

正確實現姿勢

  1. 拼接完整目標文件路徑:`File destFile = new File(targetDir, sourceFile.getName());`
  2. 優先使用 `renameTo()`:同文件系統下原子移動,無需流拷貝,極快且安全。
    `if (sourceFile.renameTo(destFile)) return true;`
  3. 跨文件系統/設備時流式拷貝:使用 `FileChannel.transferTo()` 零拷貝,或標準流分塊讀寫,最後驗證文件大小一致再刪除源文件。
  4. 權限與存儲訪問框架(SAF):Android 10+ 需請求 `MANAGE_EXTERNAL_STORAGE` 或使用 SAF `DocumentFile` API 操作共享目錄。

最佳實踐代碼片段

public static boolean moveFile(File src, File destDir) {
    if (!src.exists() || !destDir.exists() || !destDir.isDirectory()) return false;
    File dest = new File(destDir, src.getName());
    // 1. 嘗試原子重命名
    if (src.renameTo(dest)) return true;
    // 2. 降級為流拷貝
    try (FileChannel in = FileChannel.open(src.toPath(), StandardOpenOption.READ);
         FileChannel out = FileChannel.open(dest.toPath(),
             StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
        long transferred = in.transferTo(0, in.size(), out);
        if (transferred == src.length()) {
            src.delete(); // 校驗大小後再刪
            return true;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
}

關鍵提醒

永遠不要在確認寫入成功前刪除源文件;捕獲異常時務必打印堆疊跟踪,不要 `return false` 吞噬錯誤信息;適配 Android 版本差異,善用 `Context.getExternalFilesDir()` 等受保護目錄避免權限糾紛。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved