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

Android 從 URI 讀取 CSV 文件:ContentResolver openInputStream 替代 File 直接訪問

     編輯:程式設計綜合問答

Android 通過 ACTION_GET_CONTENT 選擇文件返回 content:// URI,不能直接用 File 構造路徑讀取。正確方式是用 ContentResolver.openInputStream(uri) 獲取輸入流,無論文件在 SD 卡、Downloads、雲端還是其他 App 提供。

錯誤代碼:嘗試用 File 訪問 content URI


// ❌ 錯誤:data.getPath() 返回類似 "/document/primary:Download/test.csv",非真實文件路徑
File f = new File(data.getPath()); 
FileInputStream fis = new FileInputStream(f); // FileNotFoundException

正確代碼:ContentResolver.openInputStream


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SELECT_CSV_Dialog && resultCode == RESULT_OK && data != null) {
        Uri uri = data.getData(); // content:// URI
        readCsvFromUri(uri);
    }
}

private void readCsvFromUri(Uri uri) {
    new Thread(() -> {
        try (InputStream is = getContentResolver().openInputStream(uri);
             BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
            
            String line;
            boolean firstLine = true;
            while ((line = reader.readLine()) != null) {
                if (firstLine) { firstLine = false; continue; } // 跳過表頭
                String[] columns = line.split(","); // 簡單分割,複雜 CSV 建議用 OpenCSV
                // 處理 columns...
                runOnUiThread(() -> updateUI(columns));
            }
        } catch (IOException e) {
            e.printStackTrace();
            runOnUiThread(() -> Toast.makeText(this, "讀取失敗: " + e.getMessage(), Toast.LENGTH_SHORT).show());
        }
    }).start();
}

進階:OpenCSV 庫處理複雜 CSV (引號、換行、轉義)


// 依賴: implementation 'com.opencsv:opencsv:5.8'
try (InputStream is = getContentResolver().openInputStream(uri);
     CSVReader reader = new CSVReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
    
    String[] header = reader.readNext(); // 讀取表頭
    String[] row;
    while ((row = reader.readNext()) != null) {
        // row 已正確處理引號內逗號、換行等
        String col1 = row[0];
        String col2 = row[1];
        // ...
    }
}

權限與適用性

Android 版本權限需求備註
Android 10 (API 29) 以下READ_EXTERNAL_STORAGE需運行時申請
Android 10 (API 29)無需權限 (分區存儲)僅能訪問用戶選擇的文件
Android 11+ (API 30+)無需權限僅能訪問用戶選擇的文件,ACTION_OPEN_DOCUMENT 可獲取持久權限

持久化 URI 權限 (Android 11+ 推薦)


// 使用 ACTION_OPEN_DOCUMENT 替代 ACTION_GET_CONTENT
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/csv");
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT);

// onActivityResult 中獲取持久權限
Uri uri = data.getData();
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(uri, takeFlags);
// 後續可直接用 uri 訪問,無需用戶再次選擇

常見坑點總結

  • 不要用 uri.getPath() 構造 File:content URI 路徑無意義,必須用 ContentResolver。
  • 編碼指定 UTF-8:new InputStreamReader(is, StandardCharsets.UTF_8),避免中文亂碼。
  • 耗時操作放子線程:讀取大文件會阻塞主線程導致 ANR。
  • 關閉流用 try-with-resources:自動釋放資源,防洩漏。
  • 複雜 CSV 必用 OpenCSV:手寫 split(",") 無法處理 "張三,李四",25 這類引號內逗號。

核心原則:Android 文件訪問統一用 ContentResolver + URI,拋棄 File 絕對路徑思維,擁抱「文件即內容流」的現代存儲模型。

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