尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

uni-app 里把接口文件流存到手机、再拉起系统分享

uni-app 里把接口文件流存到手机、再拉起系统分享 uni-app 里把接口文件流存到手机、再拉起系统分享插件lf-file-share1.3.0地址https://ext.dcloud.net.cn/plugin?id26490业务场景后端导出 Excel / PDF二进制文件流前端拿到ArrayBuffer用户保存到文件管理或通过系统面板分享到微信、邮件等。H5、Android、iOS、鸿蒙实现不同。本文说明分流方式、关键代码和真机问题。需求两类交互保存Android 写入公共Download目录可在系统「下载」中查看不写入Android/data/包名私有目录除非显式指定沙盒分享调起系统分享面板将文件交给其他 App两个主方法downloadOrShareArrayBuffer(buffer,filename,{appAction:save|share,mime,...})downloadOrShareByUrl(url,{appAction,filename,mime,...})appAction不传时默认share。save只写入本地并返回路径不调起分享。平台差异环境行为H5Blob 触发浏览器下载无系统分享Android 10分区存储公共目录使用 MediaStore / DownloadManagerAndroid 分享私有路径其他 App 无法读取必须使用 FileProvideriOS写入 App 沙盒分享使用UIActivityViewController鸿蒙不命中APP-PLUS不使用 plus APInvue无 DOM不能依赖document监听plusready大文件需处理 Base64 空串、禁止 JS 逐字节写 Java、写入后校验文件大小处理流程ArrayBuffer / URL │ ├─ H5 ──────────── Blob 下载 ├─ APP-PLUS ────── Android / iOSplus ├─ APP-HARMONY ─── 鸿蒙getFileSystemManager └─ uni-app x ───── utssdk/条件编译鸿蒙命中APP/APP-HARMONY不命中APP-PLUS。仅编写APP-PLUS分支时鸿蒙端无实现。// #ifdef H5downloadFileForH5(buffer,name,mime)// #endif// #ifdef APP-HARMONYreturnawaitdownloadOrShareArrayBufferHarmony(...)// #endif// #ifdef APP-PLUSawaitwaitPlusReady()// 写入公共目录或沙盒再按 appAction 分享// #endifAndroidsave默认路径/storage/emulated/0/Download/{saveDirName}/文件名写入沙盒时传saveLocation: sandbox。接口导出表格constresawaituni.request({url:https://api.example.com/export/excel,method:GET,responseType:arraybuffer// 必须设置否则拿到的不是二进制})awaitdownloadOrShareArrayBuffer(res.data,销售报表.xlsx,{appAction:save,mime:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,saveDirName:MyApp,debug:true})无后端时使用内置 mock生成可打开的最小 xlsximport{mockFetchExcelExport,downloadOrShareArrayBuffer}from/uni_modules/lf-file-shareconstresawaitmockFetchExcelExport({filename:销售报表.xlsx})awaitdownloadOrShareArrayBuffer(res.data,res.filename,{mime:res.mime,appAction:share,chooserTitle:分享表格})大文件 / 大图 mockawaitmockFetchLargeFile()// 默认 2.5MBawaitmockFetchLargeImage()// 默认 2.6MB BMP关键代码ArrayBuffer 转 Base64App 写文件需要 Base64。uni.arrayBufferToBase64在部分机型或大文件场景会返回空串或解码后字节数与buffer.byteLength不一致。处理顺序使用uni.arrayBufferToBase64校验 Base64 反推长度不一致则改用 JS 分片编码写入时按 4 的倍数分片 decodeBase64 分组要求constgetArrayBufferBase64(buffer){constexpectSizebuffer.byteLength||0letbase64cleanBase64(uni.arrayBufferToBase64(buffer))if(base64getBase64ByteLength(base64)expectSize){return{base64,source:uni.arrayBufferToBase64,byteLength:expectSize}}base64cleanBase64(arrayBufferToBase64ByChunk(buffer))// 仍不一致则抛错禁止写出空文件return{base64,source:chunkFallback,byteLength:expectSize}}禁止整包 decode禁止 JS 循环逐字节write到 Java。使用分片 BufferedOutputStream写完后校验file.length()。Android 10 写公共 Download步骤ContentValues写入DISPLAY_NAME、MIME_TYPE、RELATIVE_PATH、IS_PENDING1insert得到 UriopenOutputStream写入内容将IS_PENDING置为0失败则delete该 Uri嵌套类使用$导入否则运行时无法读取常量constDownloadsplus.android.importClass(android.provider.MediaStore$Downloads)constMediaColumnsplus.android.importClass(android.provider.MediaStore$MediaColumns)resolver.insert is not a function真机报错TypeError: resolver.insert is not a function原因getContentResolver()返回对象上的 Java 方法未挂载为 JS 函数直接调用insert得到undefined。使用plus.android.invoke兜底constinvokeAndroid(target,method,...args){if(!target)returnnullif(typeoftarget[method]function)returntarget[method](...args)returnplus.android.invoke(target,method,...args)}constresolverinvokeAndroid(main,getContentResolver)consturiinvokeAndroid(resolver,insert,collection,values)constoutputStreaminvokeAndroid(resolver,openOutputStream,uri)Cursor.moveToFirst、DownloadManager 轮询查询使用同一方式调用。Android 分享私有文件禁止直接传递file://。使用 FileProvider 生成 content UriuriFileProvider.getUriForFile(main,pkg.dc.fileprovider,file)// 失败则尝试 pkg .fileprovider再失败使用 Uri.fromFileintent.putExtra(Intent.EXTRA_STREAM,uri)main.startActivity(Intent.createChooser(intent,chooserTitle))分享失败时检查原生工程 FileProvider 与AndroidManifest配置。iOS 分享禁止只使用keyWindow.rootViewController。沿presentedViewController找到顶层 ViewController 再 present。写入使用plus.iowriteAsBinary(base64)。鸿蒙实现位于harmony.js使用uni.getFileSystemManager().writeFileSync(path, buffer)。图片使用uni.shareWithSystem其他文件使用openDocument({ showMenu: true })两者都失败时将路径写入剪贴板并抛错。nvue禁止仅使用document.addEventListener(plusready)。存在 DOM 时监听事件无 DOM 时轮询typeof plus。uni-app x插件提供utssdk/web / android / ios / harmony。业务侧引入方式import{downloadOrShareArrayBuffer}from/uni_modules/lf-file-share用法import{downloadOrShareArrayBuffer,downloadOrShareByUrl,shareOnlineImage,shareOnlineVideo,shareResource}from/uni_modules/lf-file-share// 在线 PDFconstresultawaitdownloadOrShareByUrl(https://example.com/a.pdf,{filename:说明文档,mime:application/pdf,appAction:save,saveDirName:MyApp})// 图片 / 视频awaitshareOnlineImage(url,{appAction:share})awaitshareOnlineVideo(url,{appAction:save,filename:demo.mp4,saveDirName:MyApp})// buffer 或 urlawaitshareResource({buffer,filename:报表.xlsx,appAction:save,mime:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet})参数字段说明appActionshare/savemime显式传入文件 MIMEsaveDirNameAndroid 公共 Download 下的子目录名saveLocationpublic默认或sandboxchooserTitle系统分享面板标题debug输出 Base64 转换与写入耗时返回值示例{platform:Android,action:save,path:/storage/emulated/0/Download/MyApp/a.xlsx,localPath:Download/MyApp/a.xlsx,filename:a.xlsx,size:4024,isPublic:true}真机问题未设置responseType: arraybuffer得到字符串Excel / PDF 无法打开。保存成功但「下载」中找不到文件文件写入了沙盒。Android 保存不要设置saveLocation: sandbox使用默认公共 Download。resolver.insert is not a function对 ContentResolver / Cursor 使用plus.android.invoke。大文件actual0或写入过慢禁止整包 decode禁止逐字节 write。使用分片 缓冲流写完校验长度。分享到微信失败检查 FileProvider、authority、是否使用了私有file://、mime 与文件后缀是否正确。iOS 点击分享无弹窗present 目标不是顶层 VC。取最上层presentedViewController。iPad 需配置 popover 锚点。鸿蒙引入后无效果实现写在#ifdef APP-PLUS中鸿蒙未编译该分支。nvue 中plus为 undefined未处理无 DOM 场景需轮询plus就绪。instanceof ArrayBuffer为 false使用Object.prototype.toString.call(buffer) [object ArrayBuffer]并兼容 TypedArray。文件名含\/:*?|或超长写入路径失败。插件会清洗文件名与目录名调用方也应传入合法名称。连续点击保存同名文件并发写入导致大小校验失败。按钮增加 loading禁止重复提交。在小程序中使用本插件做系统文件分享小程序无对应能力插件不支持该平台。Demo 在线 PDF 无法下载示例地址依赖外网。内网环境改用 mockpdf-demo/xlsx-report。Android 9 及以下写入公共目录失败在manifest.json配置WRITE_EXTERNAL_STORAGE。Android 10 使用 MediaStore 写入公共 Download不依赖该权限。排查开启debug: trueawaitdownloadOrShareArrayBuffer(buffer,filename,{appAction:save,mime:application/pdf,debug:true})日志输出 Base64 来源与写入耗时。sourcechunkFallback表示uni.arrayBufferToBase64校验失败已改用分片编码。console.log(buffer.byteLength,result.size,result.path)App 打开文件// #ifdef APP-PLUSplus.runtime.openFile(result.path,{},(){uni.showToast({title:未找到可打开此文件的应用,icon:none})})// #endif源码目录uni_modules/lf-file-share/ ├── index.js # H5 / APP-PLUS 主逻辑 ├── harmony.js # 鸿蒙 ├── mock.js # 本地 mock ├── utssdk/ # uni-app x ├── readme.md ├── changelog.md └── article.md演示页pages/file-share/demo.vue表格导出、大文件大图、其它 mock、在线 URL / 文本流。接入要求插件目录为uni_modules/lf-file-shareAndroid 配置 FileProvider系统分享Android 9 及以下按需配置存储权限导出接口设置responseType: arraybuffer保存 / 分享显式传入mime与文件名鸿蒙、uni-app x 在对应运行环境验证不能只用 Android 基座代替调用示例awaitdownloadOrShareArrayBuffer(res.data,销售报表.xlsx,{appAction:save,mime:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet})问题反馈插件https://ext.dcloud.net.cn/plugin?id26490邮箱lingfugroupgmail.com
返回列表