文章目录
前言
因博主近期的项目中有一个下载图片并要求保存到系统相册的需求,项目的APP是由 uni-app 项目中通过 vew-view 嵌入 H5 页面的形式。通过经过多方查找资料,分别使用了 uni-app 和 h5+ 的两种方式进行实现,本文章主要用于记录两种实现方案及遇到的一些问题。
请注意:uni-app 的方案遇到的问题没有解决方案,所以最终采取的是 H5+ 的方案
一、uni-app 实现方案
1. H5端发送消息到Web-view
使用 uni.webView 发送消息,请参考:H5发送消息
uni.webView.postMessage({
data: {
operType: 'saveImg',
url: 'http://www.test.com/download?fileId=abcd1234', // 对应实际的下载图片地址
token: '123', // 对应实际的请求下载图片的 token
// fileName: '测试.jpg'
}
});
2. web-view 接收消息并执行下载
<template>
<view>
<web-view src="/hybrid/html/index.html" @message="handleMessage"></web-view>
</view>
</template>
<script>
export default {
methods: {
handleMessage(e) {
const [opts] = e.detail.data; // e.detail.data 得到的是一个消息数组
this.downloadFile(opts);
},
// 下载文件
downloadFile(opts) {
const {
url,
token,
operType
} = opts;
uni.downloadFile({
url,
// timeout: 30000, 超时时间
header: { token }, // 下载文件需要传递的请求头参数都设置在 header 对象中
success: res => {
const {
statusCode,
tempFilePath // 临时文件路径,下载后的文件会存储到一个临时文件
} = res;
console.log(tempFilePath);
if (statusCode === 200) {
console.log('下载文件成功');
if (operType === 'saveImg') {
this.saveImg(tempFilePath);
}
} else {
uni.showToast({
title: '下载文件失败'
});
}
}
})
},
// 保存图片到相册
saveImg(filePath) {
uni.saveImageToPhotosAlbum({
filePath,
success: (path) => {
uni.showToast({ title: '已保存到系统相册' });
console.log('保存图片的绝对路径为:', path);
},
fail: (err) => {
console.log('图片保存失败', err);
}
});
},
}
</script>
- uni.downloadFile
只支持发起 get 请求
,如果需要额外的参数可拼接在 url 中。 - uni.downloadFile 的 success 回调函数中获取的 tempFilePath 是一个临时文件路径,它的前缀 _doc/ 表示这个文件位于应用的文档目录。在 Android 设备上,这个文档目录通常位于 /data/data/(你的应用包名)/files/。在 iOS 设备上,这个文档目录通常位于 <App_Home>/Documents/。然而,你通常不能直接访问这个目录,因为在移动设备上,文件系统的权限通常比较严格,应用只能在指定的目录中读写文件。
- 如果想知道文件存储的绝对路径,可调用
plus.io.convertLocalFileSystemURL(tempFilePath)
进行查看
3. 遇到的问题
- uni.downloadFile 下载的文件在安卓设备上文件名都是 downloadFile 且无后缀,uni.saveImageToPhotosAlbum 虽然调用成功,但是实际相册中不能识别到这类型的文件,导致无法查看。
- 并且 uni 提供的包括 downloadFile,saveImageToPhotosAlbum,不支持修改文件名,博主还测试了如下方法,同样没有生效,故最终采用了 H5+ 方案。
// 保存图片到相册
saveImg(filePath) {
uni.saveFile({
tempFilePath: filePath,
success: (res) => {
uni.saveImageToPhotosAlbum({
filePath: res.savedFilePath,
success: (path) => {
uni.showToast({ title: '已保存到系统相册' });
console.log('保存图片的绝对路径为:', path);
},
fail: (err) => {
console.log('图片保存失败', err);
}
});
},
fail: (err) => {
console.log('文件保存失败:' + err)
},
});
}
二、H5+ 实现方案
1. 具体实现
// h5 下载图片
plusDownloadImg(opts) {
let {
url,
token,
fileName // 文件名需要传入并且带上后缀
} = opts;
if (!fileName) fileName = '未命名.jpg'
if (url) {
let dtask = plus.downloader.createDownload(url, {
filename: '_downloads/' + fileName
}, (d, status) => {
if (status === 200) {
console.log('下载成功:' + d.filename);
// 需要转换成绝对路径
let fileSaveUrl = plus.io.convertLocalFileSystemURL(d.filename);
this.plusSaveImg(fileSaveUrl);
} else {
this.$showToast.error('下载失败');
console.log('下载失败:' + status);
}
});
dtask.setRequestHeader('token', token);
dtask.start();
}
},
// h5+ 保存图片到相册
plusSaveImg(filename) {
plus.gallery.save(filename, () => {
console.log(filename);
this.$showToast.ok('已保存到系统相册');
}, e => {
this.$showToast.error('图片保存失败');
console.log(e.message);
});
},
2. plus.downloader.createDownload 优缺点
- 下载文件支持 get、post 请求,但是 post 请求的参数放在
第二个参数的 data 选项中,但是 data 仅支持 String 类型
,所以如果请求参数是对象,得改成使用 JSON.stringify 转换成字符串,需要服务端配合进行解析。 - 支持设置下载的文件的保存路径及修改文件名称,保存文件路径仅支持以
"_downloads/"、"_doc/"、"_documents/"
开头的字符串,默认保存目录为(“_downloads”),并自动生成文件名称。如果不传 filename 选项也会出现和 uniapp 方案一样的问题,安卓设备上相册无法读取到图片(注:ios设备不指定也能正常读取)。 - createDownload 创建新的下载任务,创建成功则返回 Download 对象支持以下功能:
设置请求头:dtask.setRequestHeader()
恢复/暂停下载任务:dtask.resume() 、 dtask.pause()
计算当前的下载进度:dtask.downloadedSize / dtask.totalSize
详细信息参考:H5官网
总结
经过对比验证两种方案,因 uni-app 无法修改下载的图片文件名,导致安卓相册无法识别的问题,所以最终选取了 H5+ 的方案。