You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android使用Apache POI读取外部存储XLSM文件遇技术难题求助

Android解析本地XLSM文件解决方案

核心问题梳理

  • XLSM文件的官方MIME类型application/vnd.ms-excel.sheet.macroEnabled.12在部分系统文件管理器中不被识别,只能用*/*或application/*唤起文件选择
  • 直接传递Content URI的InputStream给WorkbookFactory可能因流生命周期、权限问题失败
  • 复制文件到缓存目录时未确保流完全写入、未正确关闭资源

解决方案步骤

1. 权限配置

在AndroidManifest.xml中添加读取外部存储权限:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />
<!-- Android 13+ 访问文档类文件仍需申请该权限 -->

针对Android 6.0+动态申请权限:

private val requestPermissionLauncher = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted ->
    if (isGranted) {
        // 权限授予后执行文件读取逻辑
    }
}

// 在按钮点击或初始化时触发权限申请
requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)

2. 直接访问固定下载目录文件(推荐,无需文件选择器)

因文件固定在下载文件夹,可直接通过文件路径访问,跳过Content URI流程:

fun readFixedXLSM() {
    val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
    val xlsmFile = File(downloadDir, "你的目标文件名.xlsm")
    
    if (!xlsmFile.exists()) {
        Log.d("xlsm", "目标文件不存在")
        return
    }

    try {
        FileInputStream(xlsmFile).use { inputStream ->
            val xlWb = WorkbookFactory.create(inputStream)
            val xlWs = xlWb.getSheet("Daily Recording") ?: run {
                Log.d("xlsm", "指定工作表不存在")
                return
            }

            for (j in 44..50) {
                val row = xlWs.getRow(j) ?: continue
                for (i in 0..11) {
                    val cell = row.getCell(i)
                    Log.d("xlsm", cell?.toString() ?: "空单元格")
                }
            }
        }
    } catch (e: IOException) {
        Log.e("xlsm", "读取文件失败: ${e.message}")
    }
}

3. 修复文件选择器的Content URI处理

若仍需使用文件选择器,确保正确复制文件到缓存并读取:

val getContent = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
    uri ?: return@registerForActivityResult

    try {
        val cacheFile = File(cacheDir, "temp_xlsm.xlsm")
        // 复制Content URI指向的文件到缓存
        contentResolver.openInputStream(uri).use { inputStream ->
            FileOutputStream(cacheFile).use { outputStream ->
                inputStream?.copyTo(outputStream)
            }
        }

        // 读取缓存中的文件
        FileInputStream(cacheFile).use { inputStream ->
            val xlWb = WorkbookFactory.create(inputStream)
            val xlWs = xlWb.getSheet("Daily Recording") ?: run {
                Log.d("xlsm", "指定工作表不存在")
                return@registerForActivityResult
            }

            for (j in 44..50) {
                val row = xlWs.getRow(j) ?: continue
                for (i in 0..11) {
                    val cell = row.getCell(i)
                    Log.d("xlsm", cell?.toString() ?: "空单元格")
                }
            }
        }

        // 读取完成后删除缓存文件(可选)
        cacheFile.delete()
    } catch (e: IOException) {
        Log.e("xlsm", "处理文件失败: ${e.message}")
    }
}

4. 关键优化点

  • 使用use函数自动关闭流,避免资源泄漏
  • 添加空值判断(工作表、行、单元格),防止空指针崩溃
  • 捕获IOException并打印错误日志,便于排查问题
  • 缓存文件使用后可删除,避免占用存储空间

MIME类型说明

部分Android系统的文件管理器无法正确识别XLSM的官方MIME类型,此时使用*/*或application/*即可正常唤起文件选择器,不影响后续读取逻辑。

内容的提问来源于stack exchange,提问作者DelitaRagout

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 06:45:30