w***@aliyun.com
w***@aliyun.com
  • 发布:2026-03-07 18:35
  • 更新:2026-03-07 18:36
  • 阅读:14

uni-app怎么保存音频文件到安卓app中

分类:uni-app

咨询大佬们一个问题,我后台接入大模型做对话,把音频文件保存到本地,是先转成成base64,然后保存的本地。base64是正确的,用其他工具可以正常转换成音频文件。但是保存到安卓手机就不行,一直报错(报错如附件),文件大小一直是0(附件截图),也没有具体报错信息。
文件目录为:/storage/emulated/0/Android/data/io.dcloud.HBuilder/apps/HBuilder/doc/temp_1772876228130_7250.wav
安卓版本:Android13 HBuilder版本4.87。
手机是小米12
写入代码如下:


            try {  
                // 4. 清理Base64前缀  
                const pureBase64 = base64Str.replace(/^data:.+;base64,/, '');  
                if (!pureBase64) {  
                    reject(new Error('Base64字符串为空'));  
                    return;  
                }  

                // 5. 生成唯一文件名 + 私有目录路径(核心:使用_doc目录)  
                const fileName = `temp_${Date.now()}_${Math.floor(Math.random() * 10000)}.${ext}`;  
                const docPath = plus.io.convertLocalFileSystemURL('_doc/'); // 私有目录  
                console.log('[FileHelper] 私有目录路径:', docPath);  

                // 6. 解析私有目录,创建文件  
                plus.io.resolveLocalFileSystemURL(  
                    '_doc/',  
                    (dirEntry) => {  
                        // 创建/打开文件  
                        dirEntry.getFile(  
                            fileName,  
                            { create: true, exclusive: false },  
                            (fileEntry) => {  
                                console.log('[FileHelper] 文件创建成功:', fileEntry.fullPath);  

                                // 7. 创建FileWriter,使用writeAsBinary写入(官方要求)  
                                fileEntry.createWriter(  
                                    (writer) => {  
                                        // 写入成功回调  
                                        writer.onwrite = () => {  
                                            console.log('[FileHelper] 文件写入成功');  

                                            // 8. 验证文件是否真实存在且非空(官方要求)  
                                            fileEntry.getMetadata(  
                                                (metadata) => {  
                                                    if (metadata.size > 0) {  
                                                        // 转换为可播放的本地路径(添加 file://协议头)  
                                                        const playPath = plus.io.convertLocalFileSystemURL(fileEntry.fullPath);  
                                                        console.log('[FileHelper] 文件路径转换成功:', playPath);  
                                                        resolve(playPath);  
                                                    } else {  
                                                        console.error('[FileHelper] 写入成功但文件大小为 0');  
                                                        reject(new Error('写入成功但文件大小为 0'));  
                                                    }  
                                                },  
                                                (err) => {  
                                                    console.error('[FileHelper] 验证文件大小失败:', err);  
                                                    reject(new Error(`验证文件大小失败:${err.message}`));  
                                                }  
                                            );  
                                        };  

                                        // 写入失败回调  
                                        writer.onerror = (e) => {  
                                            console.error('[FileHelper] 写入失败:', e);  
                                            const errorMsg = e.target?.error?.message || '未知错误';  
                                            console.error('[FileHelper] 详细错误信息:', errorMsg, '错误码:', e.target?.error?.code);  
                                            reject(new Error(`文件写入失败:${errorMsg}`));  
                                        };  

                                        // 9. Base64 转 ArrayBuffer,用 write 方法写入(核心修复)  
                                        try {  
                                            console.log('[FileHelper] ArrayBuffer 开始转换,原值:', pureBase64);  

                                            const arrayBuffer = uni.base64ToArrayBuffer(pureBase64); // 使用 uni-app 官方 API  
                                            console.log('[FileHelper] ArrayBuffer 创建成功,长度:', arrayBuffer.byteLength);  

                                            // 使用 write 方法写入 ArrayBuffer(官方推荐方式,兼容性更好)  
                                            writer.write(arrayBuffer);  
                                        } catch (e) {  
                                            console.error('[FileHelper] ArrayBuffer 转换失败:', e);  
                                            reject(new Error(`ArrayBuffer 转换失败:${e.message}`));  
                                        }  
                                    },  
                                    (err) => reject(new Error(`创建写入器失败:${err.message}`))  
                                );  
                            },  
                            (err) => reject(new Error(`创建/打开文件失败:${err.message}`))  
                        );  
                    },  
                    (err) => reject(new Error(`访问私有目录失败:${err.message}`))  
                );  
            } catch (error) {  
                console.error('[FileHelper] 异常:', error);  
                reject(new Error(`Base64转临时文件失败:${error.message}`));  
            }

相关权限配置如下:


    "app-plus" : {  
        "usingComponents" : true,  
        "nvueStyleCompiler" : "uni-app",  
        "compilerVersion" : 3,  
        "debug" : {  
            "webview" : "devtools",  
            "webviewPort" : 9223  
        },  
        "splashscreen" : {  
            "alwaysShowBeforeRender" : true,  
            "waiting" : true,  
            "autoclose" : true,  
            "delay" : 0  
        },  
        "modules" : {},  
        "distribute" : {  
            "icons" : {  
                "android" : {  
                    "hdpi" : "unpackage/res/icons/72x72.png",  
                    "xhdpi" : "unpackage/res/icons/96x96.png",  
                    "xxhdpi" : "unpackage/res/icons/144x144.png",  
                    "xxxhdpi" : "unpackage/res/icons/192x192.png"  
                },  
                "ios" : {  
                    "appstore" : "unpackage/res/icons/1024x1024.png",  
                    "ipad" : {  
                        "app" : "unpackage/res/icons/76x76.png",  
                        "app@2x" : "unpackage/res/icons/152x152.png",  
                        "notification" : "unpackage/res/icons/20x20.png",  
                        "notification@2x" : "unpackage/res/icons/40x40.png",  
                        "proapp@2x" : "unpackage/res/icons/167x167.png",  
                        "settings" : "unpackage/res/icons/29x29.png",  
                        "settings@2x" : "unpackage/res/icons/58x58.png",  
                        "spotlight" : "unpackage/res/icons/40x40.png",  
                        "spotlight@2x" : "unpackage/res/icons/80x80.png"  
                    },  
                    "iphone" : {  
                        "app@2x" : "unpackage/res/icons/120x120.png",  
                        "app@3x" : "unpackage/res/icons/180x180.png",  
                        "notification@2x" : "unpackage/res/icons/40x40.png",  
                        "notification@3x" : "unpackage/res/icons/60x60.png",  
                        "settings@2x" : "unpackage/res/icons/58x58.png",  
                        "settings@3x" : "unpackage/res/icons/87x87.png",  
                        "spotlight@2x" : "unpackage/res/icons/80x80.png",  
                        "spotlight@3x" : "unpackage/res/icons/120x120.png"  
                    }  
                }  
            },  
            "ios" : {  
                "dSYMs" : false  
            }  
        }  
    },  
    "app-android" : {  
        "distribute" : {  
            "permissions" : [  
                "<uses-permission android:name=\"android.permission.INTERNET\"/>",  
                "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",  
                "<uses-permission android:name=\"android.permission.CAMERA\"/>",  
                "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",  
                "<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",  
                "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",  
                "<uses-permission android:name=\"android.permission.READ_MEDIA_IMAGES\"/>",  
                "<uses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\"/>",  
                "<uses-permission android:name=\"android.permission.READ_MEDIA_VIDEO\"/>",  
                "<uses-permission android:name=\"android.permission.WRITE_INTERNAL_STORAGE\"/>",  
                "<uses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"/>"  
            ],  
            "android" : {  
                "package" : "com.moyin.aiteacher.chat",  
                "application" : [  
                    "<application android:requestLegacyExternalStorage=\"true\" android:exported=\"true\"/>"  
                ]  
            }  
        }  
    },  
    "uniStatistics" : {  
        "enable" : false  
    },  
    "vueVersion" : "3",  
    "locale" : "zh-Hans"
2026-03-07 18:35 负责人:无 分享
已邀请:
Ask小助手

Ask小助手 - Ask社区AI助手

欢迎到专业群( uni-app 官方技术交流群 1 ) 咨询,群中有相关专业的管理员和群友。

根据知识库中关于 Android 13 文件权限适配的说明,以及你提供的代码和配置,问题可能出在 Android 13 及以上版本对文件访问权限的变更

问题原因

在 Android 13(API 33)及以上版本中,系统引入了新的文件访问权限:

  • READ_MEDIA_IMAGES
  • READ_MEDIA_VIDEO
  • READ_MEDIA_AUDIO

这些权限取代了旧的 READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 权限。如果你的应用目标 API 是 33 或更高,且没有正确声明这些新权限,可能会导致文件写入失败或文件大小为 0 的问题。

解决方案

  1. 更新权限配置
    manifest.jsonapp-android -> distribute -> permissions 节点中,添加以下权限:

    "<uses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\"/>",  
    "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>"  
  2. 检查目标 API 版本
    确保你的 targetSdkVersion 设置为 33 或更高,以适配 Android 13 的权限变更。

  3. 使用正确的文件路径
    你当前使用的路径 /storage/emulated/0/Android/data/io.dcloud.HBuilder/apps/HBuilder/doc/ 是应用的私有目录,无需额外权限即可写入。但如果路径有误或目录未正确创建,可能导致写入失败。建议:

    • 确认 _doc/ 目录是否存在(可通过 plus.io.requestFileSystem 检查)。
    • 使用 plus.io.convertLocalFileSystemURL("_doc/") 获取正确路径。
  4. 验证 Base64 数据
    确保 pureBase64 数据完整且格式正确(无换行符或空格)。可以尝试用其他工具(如在线 Base64 解码器)验证数据是否能正常还原为音频文件。

  5. 调试建议

    • writer.onwrite 回调中打印 writer.length 确认写入的字节数。
    • 使用 plus.io.resolveLocalFileSystemURL 重新读取文件并检查其大小。

示例修正后的权限配置

"app-android": {  
  "distribute": {  
    "permissions": [  
      "<uses-permission android:name=\"android.permission.INTERNET\"/>",  
      "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",  
      "<uses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\"/>",  
      "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>"  
    ]  
  }  
}

其他注意事项

  • 如果问题仍未解决,建议检查 HBuilderX 版本是否为最新(当前正式版为 4.87,可尝试升级到 Alpha 版 5.0)。
  • 确保真机调试时授予了所有必要的运行时权限(可在系统设置中手动检查)。

如需进一步验证,可尝试将文件保存到 _downloads/ 目录(公有目录),并观察是否仍出现 0 字节问题。

内容为 AI 生成,仅供参考

要回复问题请先登录注册