// 请求存储权限
const requestStoragePermission = async () => {
if (Platform.OS === 'android') {
try {
if (Platform.Version >= 33) {
// Android 13+ 下载PDF不需要请求任何权限!
// 只需检查是否可访问下载目录
const dir = ReactNativeBlobUtil.fs.dirs.DownloadDir;
const testFile = `${dir}/permission_test_${Date.now()}.tmp`;
try {
await ReactNativeBlobUtil.fs.writeFile(testFile, 'test', 'utf8');
await ReactNativeBlobUtil.fs.unlink(testFile);
return true;
} catch {
Alert.alert(
'存储访问失败',
'无法访问下载目录,请检查系统文件权限设置',
[{text: '去设置', onPress: () => Linking.openSettings()}],
);
return false;
}
} else {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: '存储权限请求',
message: '应用需要访问存储空间以下载文件',
buttonPositive: '确定',
buttonNegative: '取消',
},
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
} catch (err) {
console.warn('请求存储权限时出错:', err);
return false;
}
}
return true; // iOS不需要显式权限
};
// 下载PDF文件
const downloadFile = async (pdfUrl: string, data: any) => {
try {
setIsDownloading(true);
setProgress(0);
// 1. 检查并请求权限
const hasPermission = await requestStoragePermission();
if (!hasPermission) {
Alert.alert('权限被拒绝', '需要存储权限才能下载文件');
return;
}
// 2. 获取文件名
let fileName = 'document.pdf';
if (data?.name) {
fileName = data?.name;
if (!fileName.toLowerCase().endsWith('.pdf')) {
fileName += '.pdf';
}
} else {
try {
// 尝试从URL获取文件名
const urlParts = pdfUrl.split('/');
const lastPart = urlParts[urlParts.length - 1];
if (lastPart.includes('.pdf')) {
fileName = decodeURIComponent(lastPart.split('?')[0]);
}
} catch (e) {
console.warn('无法从URL获取文件名,使用默认文件名');
}
}
// 3. 设置保存路径
let filePath = '';
if (Platform.OS === 'android') {
// Android: 保存到Downloads目录
filePath = `${ReactNativeBlobUtil.fs.dirs.DownloadDir}/${fileName}`;
} else {
// iOS: 保存到Documents目录
filePath = `${ReactNativeBlobUtil.fs.dirs.DocumentDir}/${fileName}`;
}
// 4. 下载配置
const config = {
fileCache: true,
path: filePath,
appendExt: 'pdf',
addAndroidDownloads: {
useDownloadManager: false, // 使用Android下载管理器
notification: true, // 显示通知
title: fileName,
description: 'PDF文件下载中...',
mime: 'application/pdf',
mediaScannable: true, // 允许媒体扫描
path: filePath, // 明确指定路径
visibleInDownloadsUi: true,
},
overwrite: true, // 覆盖已存在文件
};
// 5. 执行下载
const task = ReactNativeBlobUtil.config(config)
.fetch('GET', pdfUrl)
.progress((received, total) => {
const percent = (received / total) * 100;
setProgress(percent);
console.log(`下载进度: ${percent.toFixed(1)}%`);
});
const response = await task;
// 6. 验证下载结果
// 1.1. 检查HTTP状态码
if (response.info().status < 200 || response.info().status >= 300) {
throw new Error(`服务器返回错误状态码: ${response.info().status}`);
}
// 2.1. 检查文件是否实际存在
const fileExists = await ReactNativeBlobUtil.fs.exists(response.path());
if (!fileExists) {
throw new Error('文件下载后未找到');
}
// 3.1. 检查文件大小是否合理
const fileInfo = await ReactNativeBlobUtil.fs.stat(response.path());
console.log('文件状态:', fileInfo);
if (fileInfo.size === 0) {
throw new Error('文件大小为0,可能下载失败');
}
if (response.info().status === 200) {
// Android需要将文件添加到媒体库
if (Platform.OS === 'android') {
try {
await ReactNativeBlobUtil.MediaCollection.copyToMediaStore(
{
name: fileName,
parentFolder: '', // 空字符串表示根目录
mimeType: 'application/pdf',
},
'Download', // 文件类型为Download
filePath,
);
} catch (e) {
console.warn('添加到媒体库失败:', e);
}
}
// Alert.alert('下载成功', `PDF文件已保存到: ${filePath}`);
showCustomToast({
topOffset: -80,
textStyle: {marginLeft: 4},
message: '合同下载成功',
imageSource: Common.MessageSucIcon,
});
console.log('文件路径:', filePath);
} else {
throw new Error(`服务器返回状态码: ${response.info().status}`);
}
} catch (error: any) {
console.error('下载失败:', error);
let errorMsg = error.message;
// 提供更友好的错误提示
if (error.message.includes('403')) {
errorMsg = '没有下载权限(403)';
} else if (error.message.includes('404')) {
errorMsg = '文件不存在(404)';
} else if (error.message.includes('ENOENT')) {
errorMsg = '文件路径无效';
} else if (error.message.includes('EACCES')) {
errorMsg = '没有写入权限';
}
Alert.alert('下载失败', errorMsg);
} finally {
setIsDownloading(false);
}
};
// 打开文件
const openPDF = async (filePath: any) => {
try {
if (Platform.OS === 'android') {
// Android使用文件查看器打开
await ReactNativeBlobUtil.android.actionViewIntent(
filePath,
'application/pdf',
);
} else {
// iOS使用Document Viewer打开
await ReactNativeBlobUtil.ios.openDocument(filePath);
}
} catch (error) {
console.error('打开文件失败:', error);
Alert.alert('错误', '没有找到可以打开PDF的应用');
}
};
接口返回二进制下载的url,RN项目中如何下载保存到本地
于 2025-04-22 10:58:58 首次发布