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

Flutter中使用file_picker时如何获取文件的真实路径

Flutter中使用file_picker获取文件真实存储路径的实现方法

问题示例截图

核心原因

你拿到缓存路径并非代码错误,是file_picker的默认设计逻辑:为保证跨平台一致性、规避各系统权限限制,插件默认会将用户选中的文件拷贝到应用自身临时缓存目录,返回的path字段就是这份缓存拷贝的路径,并非文件原始存储位置。

  • iOS端受系统沙盒机制强制限制,不存在获取其他应用、系统目录下文件真实绝对路径的可能,所有第三方选择器都只能返回缓存路径或者授权访问的安全资源URL,无任何绕过方案。
  • Android端从API 29(Android 10)开始推行分区存储规则,默认禁止应用直接访问公共存储目录的文件真实路径,需要通过存储访问框架(SAF)拿到授权Uri后转换得到,且部分非媒体类文件就算拿到路径也没有直接读写权限。

Android端可行实现

方案1:基于现有file_picker扩展,无需更换选择器

只需要将file_picker返回的content格式Uri转换为真实路径即可,步骤如下:

  1. 添加依赖
    在pubspec.yaml中添加以下依赖:
dependencies:
  file_picker: ^6.1.1
  uri_to_file: ^0.2.0 # 用于将content://格式Uri转换为真实文件路径
  permission_handler: ^11.0.1 # 用于动态申请存储权限
  device_info_plus: ^9.1.1 # 用于判断Android系统版本做权限适配
  1. 配置Android清单文件
    打开android/app/src/main/AndroidManifest.xml做两处修改:
  • 在<application>标签上添加属性:android:requestLegacyExternalStorage="true",兼容Android 10版本的存储权限
  • 在<manifest>根节点下添加所需权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>
  1. 替换原有文件选择逻辑
    将你代码中的_openFileExplorer方法替换为以下实现,同时补充权限申请方法,其余原有逻辑(清除缓存、选择文件夹、UI构建代码等)不需要修改:
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:uri_to_file/uri_to_file.dart';

// 存储权限动态申请
Future<bool> _requestStoragePermission() async {
  if (!Platform.isAndroid) return true;
  final androidInfo = await DeviceInfoPlugin().androidInfo;
  if (androidInfo.version.sdkInt <= 32) {
    final status = await Permission.storage.request();
    return status.isGranted;
  } else {
    // Android 13+ 按文件类型申请对应媒体权限,非媒体文件走SAF临时授权
    final imagePerm = await Permission.photos.request();
    final videoPerm = await Permission.videos.request();
    final audioPerm = await Permission.audio.request();
    return imagePerm.isGranted || videoPerm.isGranted || audioPerm.isGranted;
  }
}

void _openFileExplorer() async {
  setState(() => _loadingPath = true);
  try {
    // 先申请权限
    final hasPerm = await _requestStoragePermission();
    if (!hasPerm) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text("未获得存储访问权限,无法选择文件"))
      );
      setState(() => _loadingPath = false);
      return;
    }
    _directoryPath = null;
    final result = await FilePicker.platform.pickFiles(
      type: _pickingType,
      allowMultiple: _multiPick,
      allowedExtensions: (_extension?.isNotEmpty ?? false)
          ? _extension?.replaceAll(' ', '').split(',')
          : null,
    );
    if (result == null) {
      setState(() => _loadingPath = false);
      return;
    }
    // 遍历文件,转换content uri为真实路径
    final List<PlatformFile> realFileList = [];
    for (final item in result.files) {
      // Android端identifier字段即为原始content:// 格式Uri
      if (item.identifier != null && item.identifier!.startsWith('content://')) {
        final realFile = await toFile(item.identifier!);
        realFileList.add(PlatformFile(
          name: item.name,
          size: item.size,
          path: realFile.path, // 此处即为文件真实存储路径
          bytes: item.bytes,
          identifier: item.identifier,
        ));
      } else {
        realFileList.add(item);
      }
    }
    _paths = realFileList;
  } on PlatformException catch (e) {
    print("操作不支持: ${e.toString()}");
  } catch (ex) {
    print("选择文件出错: $ex");
  }
  if (!mounted) return;
  setState(() {
    _loadingPath = false;
    if (_paths != null && _paths!.isNotEmpty) {
      _fileName = _paths!.map((e) => e.name).toString();
    }
  });
}

方案2:使用基于SAF实现的专属选择器

如果觉得Uri转换逻辑麻烦,可以直接替换为Android端专属的SAF框架选择器插件,这类插件选择文件后会直接返回真实路径和对应访问权限,但这类插件不支持iOS,跨平台项目需要自行做平台判断逻辑。

使用提醒

  • 不要在iOS端浪费时间尝试获取真实路径,系统沙盒机制从底层禁止跨应用访问目录,所有相关方案返回的都是应用缓存目录下的拷贝文件路径。
  • Android 11及以上版本,Download、Documents等非媒体公共目录中的文件,就算拿到真实路径,应用也没有直接读写权限,必须通过ContentResolver或者SAF提供的文件描述符操作,强行读写会直接报权限错误。
  • 如果业务场景只是需要读取文件内容、上传文件,直接使用file_picker默认返回的缓存路径即可,缓存文件不会自动删除,只有主动调用clearTemporaryFiles方法时才会被清理,完全可以满足常规业务需求,没必要强行获取真实路径。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 01:15:29