Android Studio文件选择器无法选中CSV/XLSX文件问题排查
Android文件选择器无法显示CSV/XLSX文件及解析失败问题修复
问题描述
开发一款Android应用,目标是选择CSV或Excel文件并将数据解析展示到RecyclerView,当前遇到两个核心问题:
- 点击上传按钮打开文件选择器后,无法显示设备中已存在的CSV/XLSX文件
- 移除MIME类型过滤代码后,虽能选择所有文件,但无法完成数据解析,无报错信息
测试用CSV文件内容:
Name, Price
Item1, 10
Item2, 20
Item3, 30
原问题代码
import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.annotation.SuppressLint; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.google.firebase.auth.FirebaseAuth; import com.opencsv.CSVReader; import java.io.FileReader; import java.util.ArrayList; import java.util.List; public class AdminHome extends AppCompatActivity { Button logOutBtn, uploadBtn; FirebaseAuth mAuth; TextView tvEmail; private static final int FILE_SELECT_CODE = 1; private List<MenuItem> menuItemList = new ArrayList<>(); @SuppressLint("MissingInflatedId") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_admin_home); mAuth = FirebaseAuth.getInstance(); tvEmail = findViewById(R.id.tvUser); logOutBtn = findViewById(R.id.logOutBtn); uploadBtn = findViewById(R.id.uploadBtn); String email = getIntent().getStringExtra("email"); tvEmail.setText(email); uploadBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { showFileChooser(); } }); logOutBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { signOut(); } }); } private void showFileChooser() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); // Allow all file types String[] mimeTypes = {"text/csv"}; // Add CSV MIME type intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); intent.addCategory(Intent.CATEGORY_OPENABLE); try { startActivityForResult(Intent.createChooser(intent, "Select a File"), FILE_SELECT_CODE); }catch (Exception e) { Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); System.out.println(e.getMessage()); } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode == FILE_SELECT_CODE && resultCode == RESULT_OK) { if(data != null) { Uri uri = data.getData(); String path = uri.getPath(); try { CSVReader reader = new CSVReader(new FileReader(path)); String[] line; while ((line = reader.readNext()) != null) { String name = line[0]; String price = line[1]; menuItemList.add(new MenuItem(name, price)); } RecyclerView recyclerView = findViewById(R.id.menu_list); MenuAdapter adapter = new MenuAdapter(menuItemList); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(new LinearLayoutManager(this)); reader.close(); }catch (Exception e) { Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show(); } } } } @Override public void onBackPressed() { super.onBackPressed(); finishAffinity(); } public void signOut() { mAuth.signOut(); Intent intent = new Intent(AdminHome.this, MainActivity.class); startActivity(intent); finish(); } }
问题根源及修复方案
1. 文件选择器无法显示CSV/XLSX文件
- MIME类型覆盖不全:原代码仅添加了
text/csv,但CSV可能被识别为application/csv,XLSX的标准MIME类型是application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,需补充完整:String[] mimeTypes = { "text/csv", "application/csv", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }; intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); - Intent类型兼容性:部分设备对
setType("*/*")配合EXTRA_MIME_TYPES的支持不佳,可直接设置类型为支持的MIME组合:intent.setType("text/csv|application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
2. 移除过滤后无法解析数据
- Uri路径错误:
uri.getPath()并非真实本地文件路径,ContentResolver的Uri需通过输入流读取,不能直接用FileReader:// 替换原FileReader代码 CSVReader reader = new CSVReader(new InputStreamReader(getContentResolver().openInputStream(uri))); - 缺少文件类型校验:移除过滤后可能选择非目标文件,需在解析前判断:
String mimeType = getContentResolver().getType(uri); String[] supportedTypes = {"text/csv", "application/csv", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}; if (!Arrays.asList(supportedTypes).contains(mimeType)) { Toast.makeText(this, "仅支持CSV/XLSX文件", Toast.LENGTH_SHORT).show(); return; } - Excel解析支持:原代码仅处理CSV,XLSX需引入第三方库(如Apache POI),示例解析逻辑:
XSSFWorkbook workbook = new XSSFWorkbook(getContentResolver().openInputStream(uri)); XSSFSheet sheet = workbook.getSheetAt(0); // 跳过表头行 for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); if (row != null) { String name = row.getCell(0).getStringCellValue().trim(); String price = String.valueOf(row.getCell(1).getNumericCellValue()); menuItemList.add(new MenuItem(name, price)); } } workbook.close();
优化后完整核心代码
private void showFileChooser() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); String[] mimeTypes = { "text/csv", "application/csv", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }; intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); intent.addCategory(Intent.CATEGORY_OPENABLE); try { startActivityForResult(Intent.createChooser(intent, "选择CSV或XLSX文件"), FILE_SELECT_CODE); } catch (Exception e) { Toast.makeText(this, "无法打开文件选择器: " + e.getMessage(), Toast.LENGTH_LONG).show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == FILE_SELECT_CODE && resultCode == RESULT_OK && data != null) { Uri uri = data.getData(); if (uri == null) { Toast.makeText(this, "文件路径无效", Toast.LENGTH_SHORT).show(); return; } String mimeType = getContentResolver().getType(uri); String[] supportedMimeTypes = { "text/csv", "application/csv", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }; if (!Arrays.asList(supportedMimeTypes).contains(mimeType)) { Toast.makeText(this, "仅支持CSV和XLSX文件", Toast.LENGTH_SHORT).show(); return; } menuItemList.clear(); try { if (mimeType.contains("csv")) { CSVReader reader = new CSVReader(new InputStreamReader(getContentResolver().openInputStream(uri))); String[] line; reader.readNext(); // 跳过表头 while ((line = reader.readNext()) != null) { if (line.length >= 2) { menuItemList.add(new MenuItem(line[0].trim(), line[1].trim())); } } reader.close(); } else if (mimeType.contains("spreadsheetml")) { XSSFWorkbook workbook = new XSSFWorkbook(getContentResolver().openInputStream(uri)); XSSFSheet sheet = workbook.getSheetAt(0); for (int i = 1; i <= sheet.getLastRowNum(); i++) { Row row = sheet.getRow(i); if (row != null) { String name = row.getCell(0) != null ? row.getCell(0).getStringCellValue().trim() : ""; String price = row.getCell(1) != null ? String.valueOf(row.getCell(1).getNumericCellValue()) : ""; menuItemList.add(new MenuItem(name, price)); } } workbook.close(); } RecyclerView recyclerView = findViewById(R.id.menu_list); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(new MenuAdapter(menuItemList)); Toast.makeText(this, "解析成功,共" + menuItemList.size() + "条数据", Toast.LENGTH_SHORT).show(); } catch (Exception e) { Toast.makeText(this, "解析失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); e.printStackTrace(); } } }
额外注意事项
- 若需解析XLSX,需在
build.gradle(app)中添加Apache POI依赖:implementation 'org.apache.poi:poi:5.2.3' implementation 'org.apache.poi:poi-ooxml:5.2.3' - Android 10及以上无需申请
READ_EXTERNAL_STORAGE权限,旧版本需在Manifest中声明并动态申请。
内容的提问来源于stack exchange,提问作者Naak Mukka
相关产品推荐
相关产品推荐

