Expo DocumentPicker: Android JSON 选择难题与方案
2025-02-06 15:17:27
Expo DocumentPicker在低版本安卓上选择JSON文件的处理方法
使用Expo的DocumentPicker
在React Native应用中选择文件很常见。但在一些旧版本的Android系统中,指定type: 'application/json'
后,DocumentPicker
可能无法正常选择JSON文件,尽管它们在文件管理器中可见。用户期望能够顺畅选择application/json
类型的文件,而又不希望出现其它不符合要求的文件类型。本文提供一些可行的解决方案。
问题分析
问题出在低版本安卓系统(例如Android 7、8)对于MIME类型的处理方式与DocumentPicker
和操作系统的交互方式上。即使文件后缀名为.json
,操作系统也可能无法正确识别其MIME类型为application/json
。或者,某些文件管理器可能不会向DocumentPicker
暴露所有类型的文件,即使它们理论上匹配所请求的MIME类型。
解决方案
解决思路是降低对MIME类型的要求,允许选择所有文件类型,然后在代码中进行校验,确保用户选择了正确的JSON文件。另一个思路是在低版本 Android 上,直接设置其他通用MIME类型。
方案一:不指定文件类型,手动校验
放弃在DocumentPicker
中指定type
,允许用户选择任何文件。拿到文件后,在代码中检查文件的MIME类型和内容。
import * as DocumentPicker from 'expo-document-picker';
import * as mime from 'react-native-mime-types';
import { Platform } from 'react-native';
const uploadJsonFile = async () => {
try {
const file = await DocumentPicker.getDocumentAsync({
copyToCacheDirectory: true,
});
if (!file.assets) return;
const mimeType = mime.lookup(file.assets[0].uri);
if (mimeType === 'application/json') {
// TODO: 额外安全检查:文件大小限制,内容是否符合JSON格式。
setUploadedBackup(file.assets);
} else {
alert('请选择JSON文件');
}
} catch (err) {
console.log(err);
}
};
操作步骤:
- 引入
expo-document-picker
和react-native-mime-types
依赖。 - 使用
DocumentPicker.getDocumentAsync()
时,移除type
选项。 - 通过
react-native-mime-types
的lookup()
方法获取选定文件的MIME类型。 - 验证MIME类型是否为
application/json
。 如果验证不通过,给予提示。
注意事项:
- 需要安装
react-native-mime-types
:npm install react-native-mime-types
或者yarn add react-native-mime-types
。 - 虽然移除了类型限制,但是还是需要在业务代码里面手动校验文件的合法性,避免造成非预期的错误。比如文件名,文件大小,文件内容,以及格式是否符合要求等。
方案二:指定备选 MIME 类型
针对 Android 版本进行判断,当版本低于特定值时,使用 application/octet-stream
或其他通用的 MIME 类型,而不是 application/json
。这样可以让 DocumentPicker
能够选择文件,后续再进行 MIME 类型验证。
import * as DocumentPicker from 'expo-document-picker';
import * as mime from 'react-native-mime-types';
import * as Device from 'expo-device';
import { Platform } from 'react-native';
const uploadJsonFile = async () => {
const androidVersion = Number(Device.osVersion);
const fileMimeType = androidVersion < 9 ? 'application/octet-stream' : 'application/json'; // Android 9 是一个可能的分界线
try {
const file = await DocumentPicker.getDocumentAsync({
copyToCacheDirectory: true,
type: fileMimeType,
});
if (!file.assets) return;
const mimeType = mime.lookup(file.assets[0].uri);
if (mimeType === 'application/json') {
setUploadedBackup(file.assets);
} else {
alert('请选择JSON文件');
}
} catch (err) {
console.log(err);
}
};
操作步骤:
- 引入
expo-device
用来获取安卓系统版本. 需要安装expo-device
:npx expo install expo-device
- 检查 Android 系统版本。低于某个版本时(例如Android 9),指定类型为
application/octet-stream
。 - 通过
react-native-mime-types
的lookup()
方法获取选定文件的MIME类型。 - 验证MIME类型是否为
application/json
。 如果验证不通过,给予提示。
安全建议
- 始终对用户上传的文件进行服务器端验证,确保文件内容安全,防止恶意代码注入。
- 限制文件大小,避免资源滥用。
- 对文件名进行规范化,防止XSS攻击。
这两种方案均能解决在老旧Android设备上使用DocumentPicker
选取JSON文件的问题。建议根据具体项目情况进行选择,注意做好错误处理和安全性验证,确保应用的稳定性和安全性。