Expo Media Library中createAssetAsync创建资源失败,PDF保存求助
解决expo-media-library createAssetAsync创建PDF资源失败的问题
问题场景
使用pdf-lib将图片转换为PDF,通过expo-file-system保存后,调用movePdfToDownloads函数尝试将PDF移动到设备下载文件夹时,抛出错误:Error moving the File to download folder : Could not create asset.
需要修改的关键部分
1. 补充Media Library权限申请
createAssetAsync依赖媒体库读写权限,必须先检查并申请权限,否则会直接执行失败。需在movePdfToDownloads开头添加权限校验逻辑。
2. 修正文件路径错误
原代码用FileSystem.documentDirectory + "Download/"指向App内部私有目录,Media Library无法识别该路径下的文件。需改为设备公共下载目录:
- Android:直接使用
FileSystem.downloadDirectory - iOS:无需手动指定私有目录,通过Media Library直接管理资产即可
3. 修复未定义的photoType变量
generatePdf函数中photoType变量未定义,导致图片嵌入逻辑异常,需从图片URI提取文件类型或传入该参数。
4. 修正Media Library拼写错误
代码中import * as MediaLibraray from "expo-media-library";存在拼写错误(少字母y),需改为MediaLibrary,否则所有Media Library的API调用都会失效。
修改后的完整代码
import * as FileSystem from "expo-file-system"; import * as MediaLibrary from "expo-media-library"; // 修正拼写错误 import { PDFDocument } from "pdf-lib"; import { Buffer } from "buffer"; import { Platform } from "react-native"; // 新增平台判断依赖 export const generatePdf = async (imageUri, dimensions) => { global.Buffer = Buffer; try { // 从图片URI提取文件类型(解决photoType未定义问题) const photoType = imageUri.split('.').pop().toLowerCase(); const pdfDoc = await PDFDocument.create(); const imageBytes = await FileSystem.readAsStringAsync(imageUri, { encoding: FileSystem.EncodingType.Base64, }); // 根据实际文件类型嵌入图片(修正条件判断逻辑) const embeddedImage = photoType === "jpg" || photoType === "jpeg" ? await pdfDoc.embedJpg(imageBytes) : await pdfDoc.embedPng(imageBytes); const page = pdfDoc.addPage([dimensions.width, dimensions.height]); const scaleFactor = Math.min( dimensions.width / embeddedImage.width, dimensions.height / embeddedImage.height ); const { width, height } = embeddedImage.scale(scaleFactor); page.drawImage(embeddedImage, { x: (page.getWidth() - width) / 2, y: (page.getHeight() - height) / 2, width, height, }); const pdfBytes = await pdfDoc.save(); const pdfBase64 = Buffer.from(pdfBytes).toString("base64"); const fileUri = `${FileSystem.documentDirectory}generated_image.pdf`; await FileSystem.writeAsStringAsync(fileUri, pdfBase64, { encoding: FileSystem.EncodingType.Base64, }); return fileUri; } catch (error) { console.error("Error generating the PDF : ", error.message); throw new Error("Failed to generate PDF !"); } }; export const movePdfToDownloads = async (pdfPath) => { try { // 检查并申请Media Library权限 const { status } = await MediaLibrary.requestPermissionsAsync(); if (status !== 'granted') { throw new Error("需要媒体库读写权限才能保存PDF"); } const fileInfo = await FileSystem.getInfoAsync(pdfPath); if (!fileInfo.exists) { throw new Error(`指定文件不存在: ${pdfPath}`); } // 根据平台处理文件路径 let downloadPath; if (Platform.OS === 'android') { // Android使用公共下载目录 downloadPath = `${FileSystem.downloadDirectory}generated.pdf`; await FileSystem.copyAsync({ from: pdfPath, to: downloadPath }); } else { // iOS直接使用App内生成的文件路径创建资产 downloadPath = pdfPath; } // 创建媒体资产 const asset = await MediaLibrary.createAssetAsync(downloadPath); // 获取或创建Download相册 let album = await MediaLibrary.getAlbumAsync("Download"); if (!album) { album = await MediaLibrary.createAlbumAsync("Download", asset); } else { await MediaLibrary.addAssetsToAlbumAsync([asset], album, false); } console.log(`PDF已成功保存到下载文件夹: ${downloadPath}`); return downloadPath; } catch (error) { console.error("Error moving the File to download folder :", error.message); throw new Error(`Failed to move the PDF to downloads : ${error.message}`); } };
额外说明
- iOS中,Media Library的
Download相册是系统默认相册,无需手动创建路径,直接使用App内部生成的文件路径即可创建资产。 - Android中,
FileSystem.downloadDirectory指向设备公共下载目录,该目录下的文件会直接显示在系统文件管理器的下载文件夹中。
内容的提问来源于stack exchange,提问作者HETH CHHEDA
相关产品推荐
相关产品推荐

