Android 10及以上设备无法创建文件/目录问题求助(附代码)
问题修复方案:Android 10无法创建目录及文件读取问题
核心问题分析
Android 10引入了分区存储(Scoped Storage),即使添加了android:requestLegacyExternalStorage="true",直接使用mkdir()创建目录可能因权限细节或路径处理失败;同时,Android 10及以上版本对Uri的直接路径解析不再可靠,导致文件读取异常。
修改后的完整代码
void Convert_to_PDF(Uri path) throws IOException, DocumentException { // 1. 修复目录创建逻辑:改用mkdirs()并添加错误日志 File storageDir = new File(Environment.getExternalStorageDirectory() + "/MultiFileCon"); if (!storageDir.exists()) { boolean isDirCreated = storageDir.mkdirs(); if (!isDirCreated) { Log.e("PDF_CONVERT", "目录创建失败:" + storageDir.getAbsolutePath()); Toast.makeText(this, "无法创建存储目录,请检查权限", Toast.LENGTH_SHORT).show(); return; } } // 生成带时间戳的PDF文件名 SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH.mm.ss", Locale.getDefault()); String pdfFileName = formatter.format(new Date()) + ".pdf"; File pdffilex = new File(storageDir.getAbsolutePath(), pdfFileName); Log.d("PDF_PATH", "生成的PDF路径:" + pdffilex.getAbsolutePath()); // 2. 修复Android 10+的文件读取逻辑:用ContentResolver替代路径解析 InputStream input_document; if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { Toast.makeText(this, "Android版本:" + android.os.Build.VERSION.SDK_INT, Toast.LENGTH_LONG).show(); Log.d("VERSION", "当前版本:Android 10+"); // 通过ContentResolver直接获取文件输入流,避免Uri路径解析问题 input_document = getContentResolver().openInputStream(path); } else { input_document = new FileInputStream(new File(PathUtil.getPath(this, path))); } // Excel转PDF核心逻辑 HSSFWorkbook my_xls_workbook = new HSSFWorkbook(input_document); HSSFSheet my_worksheet = my_xls_workbook.getSheetAt(0); Iterator<Row> rowIterator = my_worksheet.iterator(); Document iText_xls_2_pdf = new Document(); PdfWriter.getInstance(iText_xls_2_pdf, new FileOutputStream(pdffilex)); iText_xls_2_pdf.open(); PdfPTable my_table = new PdfPTable(7); PdfPCell table_cell; while (rowIterator.hasNext()) { Row row = rowIterator.next(); Iterator<Cell> cellIterator = row.cellIterator(); while (cellIterator.hasNext()) { Cell cell = cellIterator.next(); switch (cell.getCellType()) { case Cell.CELL_TYPE_STRING: table_cell = new PdfPCell(new Phrase(cell.getStringCellValue())); my_table.addCell(table_cell); break; // 新增:处理数字、日期类型的单元格 case Cell.CELL_TYPE_NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { table_cell = new PdfPCell(new Phrase(formatter.format(cell.getDateCellValue()))); } else { table_cell = new PdfPCell(new Phrase(String.valueOf(cell.getNumericCellValue()))); } my_table.addCell(table_cell); break; } } } iText_xls_2_pdf.add(my_table); iText_xls_2_pdf.close(); input_document.close(); my_xls_workbook.close(); // 释放Workbook资源 Toast.makeText(this, "PDF已生成:" + pdfFileName, Toast.LENGTH_LONG).show(); }
关键修改点说明
- 目录创建优化
- 替换
mkdir()为mkdirs():确保父目录不存在时也能递归创建 - 添加错误日志和Toast提示:便于排查目录创建失败的具体原因
- 替换
- Android 10+文件读取修复
- 放弃Uri路径拆分的方式,改用
getContentResolver().openInputStream(path)直接获取文件流,完全适配分区存储规则 - 版本判断改为
>= Build.VERSION_CODES.Q(对应Android 10,SDK 29),更准确
- 放弃Uri路径拆分的方式,改用
- 其他优化
- 新增
Locale.getDefault()避免SimpleDateFormat的本地化问题 - 添加Excel数字/日期类型的处理逻辑,提升兼容性
- 关闭
HSSFWorkbook释放资源,避免内存泄漏 - 增加PDF生成成功的提示
- 新增
额外注意事项
- 确保Dexter权限申请逻辑正确,在调用
Convert_to_PDF前已获取WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE权限 - 若后续升级到Android 11+,建议逐步迁移到MediaStore API存储文件,彻底适配分区存储
内容的提问来源于stack exchange,提问作者vikram Choudhary
相关产品推荐
相关产品推荐

