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

解决ActivityResultContracts.CreateDocument()获取文件类型为null问题

问题原因

拿不到扩展名的核心原因有两个:

  • 你使用了ActivityResultContracts.CreateDocument的无参构造方法,该方法已经被废弃,没有传入默认MIME类型时,系统返回的content:// Uri 大概率不会携带正确的MIME信息,直接导致contentResolver.getType(uri)返回null。
  • 依赖MimeTypeMap从MIME类型反推扩展名本身就不可靠,不同文档提供方(系统文件管理器、第三方文件App、云盘等)返回的MIME类型可能错漏、缺失,完全不适合作为判断文件格式的依据。
正确实现方案

不要从MIME类型反推格式,直接通过ContentResolver查询Uri对应的DISPLAY_NAME字段拿到完整文件名,再从文件名中解析扩展名,这个方案对所有合法的content Uri生效,不受提供方实现影响。

第一步:修正Launcher初始化

给CreateDocument传入默认MIME类型,比如保存图片就传"image/*",如果固定保存png可以传"image/png":

private ActivityResultLauncher<String> activityResultLauncher =
        registerForActivityResult(new ActivityResultContracts.CreateDocument("image/*"),
                uri -> {
                    if (uri == null) return;
                    Bitmap bitmap = cameraViewModel.getImage();
                    if (bitmap == null) return;
                    
                    ContentResolver contentResolver = mainActivity.getContentResolver();
                    // 拿到完整文件名
                    String fileName = getFileNameFromUri(contentResolver, uri);
                    // 解析扩展名
                    String ext = getFileExtension(fileName);
                    // 匹配压缩格式
                    Bitmap.CompressFormat format = getCompressFormatByExt(ext);
                    
                    // 自动关流,不用手动写close
                    try (OutputStream outputStream = contentResolver.openOutputStream(uri)) {
                        if (outputStream != null) {
                            // 质量参数:PNG是无损的,质量参数会被忽略,JPEG/WebP按需求传0-100
                            int quality = (format == Bitmap.CompressFormat.JPEG) ? 90 : 100;
                            bitmap.compress(format, quality, outputStream);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });

第二步:实现工具方法

从Uri查询文件名

private String getFileNameFromUri(ContentResolver resolver, Uri uri) {
    String fileName = null;
    // 处理content协议的Uri
    if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
        try (Cursor cursor = resolver.query(
                uri,
                new String[]{OpenableColumns.DISPLAY_NAME},
                null,
                null,
                null
        )) {
            if (cursor != null && cursor.moveToFirst()) {
                int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
                if (nameIndex != -1) {
                    fileName = cursor.getString(nameIndex);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 兜底处理file协议等特殊情况
    if (fileName == null) {
        fileName = uri.getLastPathSegment();
    }
    return fileName;
}

从文件名解析扩展名

private String getFileExtension(String fileName) {
    if (fileName == null) return "";
    int dotIndex = fileName.lastIndexOf(".");
    if (dotIndex == -1 || dotIndex == fileName.length() - 1) {
        return "";
    }
    return fileName.substring(dotIndex + 1).toLowerCase(Locale.ROOT);
}

根据扩展名匹配压缩格式

private Bitmap.CompressFormat getCompressFormatByExt(String ext) {
    switch (ext) {
        case "jpg":
        case "jpeg":
            return Bitmap.CompressFormat.JPEG;
        case "webp":
            // 适配不同SDK版本的WebP格式,API30以上可以用无损WEBP
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                return Bitmap.CompressFormat.WEBP_LOSSLESS;
            } else {
                return Bitmap.CompressFormat.WEBP;
            }
        case "png":
        default:
            // 默认用PNG,支持透明通道,兼容性最好
            return Bitmap.CompressFormat.PNG;
    }
}

第三步:调用Launcher时传入默认文件名

建议在调用launch时传入带默认扩展名的文件名提示,减少用户手动输入错误的概率:

// 比如默认建议保存为png格式,文件名带时间戳避免重复
activityResultLauncher.launch("IMG_" + System.currentTimeMillis() + ".png");
注意事项
  • 不要依赖contentResolver.getType()的返回值判断格式,这个值完全由文档提供方决定,很多场景下会返回null或者错误类型。
  • 流操作统一用try-with-resources语法,不需要手动调用close(),避免异常场景下流泄漏。
  • PNG格式是无损压缩,compress方法的quality参数对PNG无效,传100即可。
  • 如果需要支持更多格式(比如HEIC),可以自己在getCompressFormatByExt方法里加对应判断,注意API版本兼容性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 00:27:27