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

如何消除Android端Expo Go拍照弹出的“允许修改此照片”提示

问题背景

开发React Native Expo应用时,使用expo-image-picker实现拍照功能,通过expo-media-library将拍摄的图片保存到自定义目录,功能整体运行正常,但在Android设备上,用户每次拍摄图片后都会弹出*"Allow Expo Go to modify this photo?"*的弹窗,严重影响用户体验。

依赖版本信息

  • react-native: 0.64.3
  • expo: ~44.0.0
  • expo-image-picker: ~12.0.2
  • expo-media-library: ~14.0.0

原有功能实现代码

import * as ImagePicker from "expo-image-picker";
import * as MediaLibrary from "expo-media-library";

const savePhoto = async (data, onSucess) => {
  const asset = await MediaLibrary.createAssetAsync(data?.uri);
  const album = await MediaLibrary.createAlbumAsync(
    "TEST_FOLDER",
    asset,
    false
  );
  const albumAssets = await MediaLibrary.getAssetsAsync({
    album: album,
    first: 1,
    sortBy: [[MediaLibrary.SortBy.creationTime, false]],
  });
  if (albumAssets?.assets[0]) {
    onSucess(albumAssets.assets[0], data);
  }
};

const takePicture = async (onSucess, onError) => {
  let data = await ImagePicker.launchCameraAsync({
    mediaTypes: ImagePicker.MediaTypeOptions.Images,
    quality: 0.8,
    allowsEditing: true,
    base64: true,
  });
  if (data.cancelled === false) {
    savePhoto(data, onSucess);
  } else {
    onError();
  }
};

原有app.json配置

{
  "expo": {
    "name": "product",
    "slug": "product_frontend",
    "version": "1.0.0",
    "orientation": "landscape",
    "icon": "./assets/images/icon.png",
    "userInterfaceStyle": "automatic",
    "splash": {
      "image": "./assets/images/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "plugins": [
      [
        "expo-media-library",
        {
          "photosPermission": "Allow $(PRODUCT_NAME) to access your photos.",
          "savePhotosPermission": "Allow $(PRODUCT_NAME) to save photos.",
          "isAccessMediaLocationEnabled": "true"
        }
      ],
      [
        "expo-image-picker",
        {
          "cameraPermission": "Allow $(PRODUCT_NAME) to access your camera",
          "photosPermission": "The app accesses your photos to let you share them with your friends."
        }
      ]
    ],
    "updates": {
      "fallbackToCacheTimeout": 0
    },
    "assetBundlePatterns": ["**/*"],
    "ios": {
      "supportsTablet": true
    },
    "android": {
      "package": "com.comapny.product",
      "adaptiveIcon": {
        "foregroundImage": "./assets/images/adaptive-icon.png",
        "backgroundColor": "#FFFFFF"
      },
      "softwareKeyboardLayoutMode": "pan",
      "permissions": []
    },
    "web": {
      "favicon": "./assets/images/favicon.png"
    },
    "scheme": "product"
  }
}
问题原因

该弹窗由Android 10及以上版本的分区存储(Scoped Storage)机制触发:

  1. 开启allowsEditing: true参数后,相机拍摄并编辑后的临时图片存储在系统相机/Expo Go的私有缓存目录,不属于当前应用的自有存储路径。
  2. 直接调用MediaLibrary接口将该路径下的文件写入公共媒体目录时,系统会判定应用正在修改非自身所有的媒体文件,每次操作都会弹出临时授权确认弹窗。
  3. 现有配置中Android节点的permissions数组为空,未声明完整的媒体访问权限,无法获得媒体目录的长期访问授权。
  4. 原有代码每次保存时都调用createAlbumAsync,重复触发目录写入的权限校验,也会增加弹窗触发概率。
解决方案

按以下步骤调整即可消除重复弹窗:

  1. 按需关闭编辑参数
    如果业务不需要用户拍摄后在相机界面裁剪图片,直接将launchCameraAsync中的allowsEditing参数设为false,拍摄后的临时文件路径归属于当前应用,不会触发跨目录修改校验,该操作可解决80%以上的弹窗问题。
  2. 补全Android权限声明
    修改app.json中Android节点的permissions配置,添加必要的媒体访问权限,修改后配置如下:
    "android": {
      "package": "com.comapny.product",
      "adaptiveIcon": {
        "foregroundImage": "./assets/images/adaptive-icon.png",
        "backgroundColor": "#FFFFFF"
      },
      "softwareKeyboardLayoutMode": "pan",
      "permissions": [
        "READ_EXTERNAL_STORAGE",
        "WRITE_EXTERNAL_STORAGE",
        "ACCESS_MEDIA_LOCATION"
      ]
    }
    
  3. 调整文件保存逻辑
    引入expo-file-system依赖,先将相机返回的临时文件复制到应用自有缓存目录,再从自有目录创建媒体资源,避免直接操作其他应用目录下的文件;同时优化相册创建逻辑,相册已存在时直接添加资源,不要重复调用创建接口。修改后的保存逻辑代码如下:
    import * as ImagePicker from "expo-image-picker";
    import * as MediaLibrary from "expo-media-library";
    import * as FileSystem from "expo-file-system";
    
    const savePhoto = async (data, onSucess) => {
      // 生成应用缓存目录下的目标文件路径
      const fileExt = data.uri.split('.').pop();
      const tempAppPath = `${FileSystem.cacheDirectory}temp_camera_${Date.now()}.${fileExt}`;
      
      // 将相机返回的临时文件复制到应用自有目录
      await FileSystem.copyAsync({
        from: data.uri,
        to: tempAppPath
      });
    
      // 从应用自有路径创建媒体资源
      const asset = await MediaLibrary.createAssetAsync(tempAppPath);
      
      // 检查相册是否存在,不存在再创建
      let album = await MediaLibrary.getAlbumAsync("TEST_FOLDER");
      if (!album) {
        album = await MediaLibrary.createAlbumAsync("TEST_FOLDER", asset, false);
      } else {
        await MediaLibrary.addAssetsToAlbumAsync([asset], album, false);
      }
    
      const albumAssets = await MediaLibrary.getAssetsAsync({
        album: album,
        first: 1,
        sortBy: [[MediaLibrary.SortBy.creationTime, false]],
      });
      if (albumAssets?.assets[0]) {
        onSucess(albumAssets.assets[0], data);
      }
    
      // 清理临时缓存文件
      await FileSystem.deleteAsync(tempAppPath, { idempotent: true });
    };
    
    // takePicture方法逻辑保持不变即可
    const takePicture = async (onSucess, onError) => {
      let data = await ImagePicker.launchCameraAsync({
        mediaTypes: ImagePicker.MediaTypeOptions.Images,
        quality: 0.8,
        allowsEditing: true, // 如需保留编辑能力无需修改该参数
        base64: true,
      });
      if (data.cancelled === false) {
        savePhoto(data, onSucess);
      } else {
        onError();
      }
    };
    
  4. 重新构建应用
    配置修改完成后,执行expo prebuild --clean清理旧的原生配置缓存,再重新启动开发环境。注意官方Expo Go客户端存在签名和权限限制,若测试时仍有弹窗,可构建自定义开发客户端(dev client)或正式安装包测试,权限配置即可完全生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:06:21