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

Android中从PDF文件URI获取真实路径报错,如何解决?

问题描述

我想从PDF文件的URI获取真实路径,实现代码如下:

文件处理代码

@NonNull
private MultipartBody.Part prepareFilePart(Uri fileUri) {
    File file = new File(getPDFPath(fileUri));
    RequestBody requestFile = RequestBody.create(MediaType.parse("*/*"),file);
    return MultipartBody.Part.createFormData("pdf", file.getName(), requestFile);
}

public String getPDFPath(Uri uri){
    final String id = DocumentsContract.getDocumentId(uri);
    final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
    String[] projection = { MediaStore.Images.Media.DATA };
    Cursor cursor = getContentResolver().query(contentUri, projection, null, null, null);
    int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    cursor.moveToFirst();
    return cursor.getString(column_index);
}

URI获取代码

ActivityResultLauncher<Intent> intentresultlauncher = registerForActivityResult(
    new ActivityResultContracts.StartActivityForResult(),
        result -> {
            if (result.getResultCode() == Activity.RESULT_OK) {
                Intent data = result.getData();
                if(data != null){
                    Uri pdf_data = data.getData();
                    prepareFilePart(pdf_data);
                }
            }
        }
);
button.setOnClickListener(view -> {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");
    intentresultlauncher.launch(intent);
});

运行时出现错误:

java.lang.NumberFormatException: For input string: "document:1000011543"

请问哪里出错了?该如何解决?或者有没有其他获取文件真实路径的方法?


错误原因分析
  1. 数字格式异常直接原因:DocumentsContract.getDocumentId(uri)返回的字符串是document:1000011543,直接用Long.parseLong(id)转换时,前缀document:会导致数字格式解析失败。
  2. 隐藏适配问题:
    • 硬编码content://downloads/public_downloads仅适用于系统下载目录的文件,但ACTION_OPEN_DOCUMENT返回的URI可能来自第三方文件管理器、云存储等其他提供者,通用性极差。
    • 使用MediaStore.Images.Media.DATA字段获取路径已过时,Android 10及以上版本受Scoped Storage限制,该字段可能返回空或无法访问的路径。
解决方法

方案1:修复当前路径获取逻辑(仅适用于下载目录文件)

先剥离文档ID的前缀,再做数字转换,同时修正MediaStore字段:

public String getPDFPath(Uri uri){
    String id = DocumentsContract.getDocumentId(uri);
    // 剥离"document:"前缀,提取纯数字ID
    if (id.startsWith("document:")) {
        id = id.split(":")[1];
    }
    final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
    // 用MediaStore.Downloads而非Images,匹配PDF文件类型
    String[] projection = { MediaStore.Downloads.DATA };
    Cursor cursor = getContentResolver().query(contentUri, projection, null, null, null);
    if (cursor == null) return null;
    int column_index = cursor.getColumnIndexOrThrow(MediaStore.Downloads.DATA);
    cursor.moveToFirst();
    String path = cursor.getString(column_index);
    cursor.close(); // 关闭Cursor避免内存泄漏
    return path;
}

方案2:通过文件流创建临时文件(通用推荐)

Android 10+官方不建议直接获取文件真实路径,更安全的方式是通过ContentResolver读取文件流,转换为临时文件后用于Multipart请求:

@NonNull
private MultipartBody.Part prepareFilePart(Uri fileUri) throws IOException {
    // 获取文件名
    String fileName = getFileName(fileUri);
    // 在缓存目录创建临时PDF文件
    File tempFile = File.createTempFile("temp_pdf", ".pdf", getCacheDir());
    // 将Content URI的文件流复制到临时文件
    try (InputStream inputStream = getContentResolver().openInputStream(fileUri);
         FileOutputStream outputStream = new FileOutputStream(tempFile)) {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
    }
    // 构建Multipart请求体
    RequestBody requestFile = RequestBody.create(MediaType.parse("application/pdf"), tempFile);
    return MultipartBody.Part.createFormData("pdf", fileName, requestFile);
}

// 从Content URI获取文件名的辅助方法
private String getFileName(Uri uri) {
    String fileName = null;
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    if (cursor != null) {
        cursor.moveToFirst();
        fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
        cursor.close();
    }
    return fileName != null ? fileName : "unknown.pdf";
}

该方案优势:

  • 适配所有ACTION_OPEN_DOCUMENT返回的URI,包括云存储、第三方文件管理器文件
  • 符合Android 10+的Scoped Storage规范,无需额外文件权限
  • 避免路径获取失败的兼容性问题
额外注意事项
  • 使用临时文件后,建议在请求完成后调用tempFile.delete()清理缓存,避免占用空间。
  • 若minSdkVersion >=29,可尝试使用MediaStore.Downloads.RELATIVE_PATH替代DATA字段,但流转换方案通用性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 22:38:27