Bootstrap

uni-app、H5+ 实现下载图片并保存到相册


前言

因博主近期的项目中有一个下载图片并要求保存到系统相册的需求,项目的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+ 的方案。

;