实现微信小程序内嵌H5中的下载功能

  • 一、项目场景:
    • 难点
    • 解决方案:
      • 1、H5微信小程序:
        • a、首先必不可少的是安装jweixin-module模块:
        • b、在main.js中将依赖绑定:
        • c、H5对应页面点击下载时代码为:
      • 2、uni-app的小程序
        • a、对应的/pages/study/downLoading页面
        • b、可以预览的时候返回到web-view页面:
  • 二、注意(下载大文件)
    • 具体步骤如下:
      • 1.获取文件的总大小。
      • 2.将文件分成若干个块(BLOCK_SIZE 为每个块的大小),以便分批下载。
      • 3.循环下载每一块。
    • 完整代码(自己改了改)
  • 总结:

一、项目场景:

微信小程序的开发框架是uniapp,使用uniapp脚手架搭建,其中有页面是展示另一个小程序,在这个页面主体内容使用了标签将H5的页面内容展示,H5中有页面存放了下载的路径。点击下载按钮下载文件,或者预览文件让用户手动保存。

难点

  • 如果是pc端,下载用一个<a>标签就很容易,但是在小程序里的标签中是行不通的。
  • 此外,小程序里的<web-view>与小程序的通行方式主要用postMessage,但是触发条件非常苛刻,参照微信的官方文档,只在小程序后退组件销毁,还有分享时才会触发postMessage并一次性把值全部带出来,使用起来非常不便,对于小程序实际内容主体是<web-view>内嵌的spa的H5页面的情况下,销毁组件会带来很多麻烦,因此最后放弃了这个方案

解决方案:

1、H5微信小程序:

a、首先必不可少的是安装jweixin-module模块:

npm i jweixin-module

b、在main.js中将依赖绑定:

import wx from  "jweixin-module"
Vue.prototype.$wx = wx

c、H5对应页面点击下载时代码为:

this.$wx.miniProgram.navigateTo({url:"/pages/study/downLoading?url="+encodeURIComponent(fileInfo.content_url)
})

2、uni-app的小程序

a、对应的/pages/study/downLoading页面

<template> <div>下载中。。。</div> </template>
<script lang="ts" setup>
import { onLoad } from '@dcloudio/uni-app';
onLoad((option: any) => {console.log('gfdjhwghjghjkjghjklghjk', option.url);handleDown(decodeURIComponent(option.url));
});
const handleDown = (url: string) => {uni.showLoading({title: '下载中', //提示文字mask: true, //是否显示透明蒙层,防止触摸穿透,默认:false});console.log(url);let imageType = ['gif', 'png', 'jpg', 'tif', 'bmp', 'webp', 'jpeg', 'JPG'];const downloadTask = uni.downloadFile({url: url,success: res => {console.log(res, 'resresresres');uni.hideLoading();if (res.statusCode === 200) {console.log(res.tempFilePath, '1111111');let originFileNameSplit = res.tempFilePath.split('.');let fileNameType = originFileNameSplit[originFileNameSplit.length - 1];uni.getFileSystemManager().saveFile({//微信保存文件,这个存储有点复杂// 临时存储路径,先有临时存储路径方可使用wx官方的存储本地路径( uni.env.USER_DATA_PATH )tempFilePath: res.tempFilePath,//定义本地的存储路径及名称success(res) {const savedFilePath = res.savedFilePath;uni.setStorageSync('uploadData', savedFilePath); //保存传入的参数uni.setStorageSync('isImageType', imageType.includes(fileNameType)); //保存传入的参数uni.hideLoading();uni.navigateBack({delta: 1,});},fail(err) {uni.showToast({title: '预览失败',icon: 'error',duration: 1500,});setTimeout(() => {uni.navigateBack({delta: 1,});}, 3000);},});}},fail: err => {uni.hideLoading();uni.showToast({title: '下载失败',icon: 'error',duration: 1500,});console.log(err);setTimeout(() => {uni.navigateBack({delta: 1,});}, 3000);},});downloadTask.onProgressUpdate(res => {// console.log('下载进度', res.progress);// console.log('已经下载的数据长度', res.totalBytesWritten);// console.log('预期需要下载的数据总长度', res.totalBytesExpectedToWrite);});
};
</script>

b、可以预览的时候返回到web-view页面:

<template><view class="study"><web-view class="webviewOut" :src="你的url地址"></web-view></view>
</template><script setup lang="ts">
onShow(() => {const url = uni.getStorageSync('uploadData');console.log('1111111111111111111111111111111111', url);if (url) {uni.hideHomeButton();handleDown(url); //具体的微信下载文件的方法//每次onShow执行完,还有上面的下载方法执行完后要把这个标记重置为false,这样不同情况触发的onShow才能区分是否是下载文件页面回来的。可能写的重复但是多写几次比较放心}
});
const handleDown = (savedFilePath: string) => {const isImageType = uni.getStorageSync('isImageType');if (isImageType) {uni.previewImage({urls: [savedFilePath],success: function (res) {uni.removeStorageSync('uploadData');uni.removeStorageSync('isImageType');},fail: function (err) {console.log(res);uni.showToast({title: '预览失败',icon: 'error',duration: 1500,});uni.removeStorageSync('uploadData');uni.removeStorageSync('isImageType');},});} else {uni.openDocument({//微信打开文件filePath: savedFilePath,showMenu: true,success: function (res) {uni.removeStorageSync('uploadData');uni.removeStorageSync('isImageType');},fail: function (err) {console.log(res);uni.showToast({title: '预览失败',icon: 'error',duration: 1500,});uni.removeStorageSync('uploadData');uni.removeStorageSync('isImageType');},});}
};
</script><style scoped lang="scss">
.study {height: 100vh;box-sizing: border-box;padding-bottom: 98rpx;
}
</style>

二、注意(下载大文件)

uni.downloadFile 主要包含两个参数:urlfilePath,其中 url 代表下载文件的路径,filePath 代表下载文件保存的路径。
uni.downloadFile 可以用来下载较小的文件,但是对于大文件的下载需要通过 分片下载方式 。具体来说,可以使用 uni.downloadFile 和 uni.uploadFile 结合使用实现分片下载。同时,需要注意为下载文件分配足够的存储空间,以便能够完整保存整个文件。

具体步骤如下:

1.获取文件的总大小。

uni.request({url: 'http://example.com/fileUrl',method: 'HEAD',success: function (res) {let contentLength = res.header['Content-Length'];// 计算出下载总块数let blockCount = Math.ceil(contentLength / BLOCK_SIZE);// 创建一个和块数相同的数组let blockList = new Array(blockCount).fill(0);// 进行分块下载downloadFileByBlock(url, savePath, blockList);}
});

2.将文件分成若干个块(BLOCK_SIZE 为每个块的大小),以便分批下载。

3.循环下载每一块。

function downloadFileByBlock(url, savePath, blockList) {blockList.forEach(function (v, i) {let rangeStart = i * BLOCK_SIZE;let rangeEnd = (i + 1) * BLOCK_SIZE - 1;// 利用 Range 请求头指定下载区间uni.downloadFile({url: url,filePath: savePath,header: {'Range': 'bytes=' + rangeStart + '-' + rangeEnd},success: function (res) {console.log(res);},fail: function (err) {console.log(err);}});});
}

大文件上传也可以采用类似的方式, 即将文件分成若干块,每次上传一个块,直到上传完整个文件。

完整代码(自己改了改)

这里给出针对微信小程序 web-view 内嵌 H5 中大文件切片下载的完整代码实现:

// download.jsconst downloadUrl = 'http://example.com/files/file.zip' // 下载地址
const fileName = 'file.zip' // 下载的文件名
const fileSize = 1024 * 1024 * 100 // 文件大小,100MB
const chunkSize = 1024 * 1024 * 2 // 分片大小,2MB
const tempFilePath = wx.env.USER_DATA_PATH + '/downloads/' // 临时文件存储路径Page({data: {progress: 0, // 下载进度isDownloading: false, // 是否正在下载downloadTask: null // 下载任务句柄},// 开始下载startDownload() {if (this.data.isDownloading) {return}this.setData({ isDownloading: true })this.downloadChunks()},// 取消下载cancelDownload() {if (this.data.downloadTask) {this.data.downloadTask.abort()}this.resetState()},// 处理分片下载任务队列,完成后进行合并操作downloadChunks() {const chunkNum = Math.ceil(fileSize / chunkSize)const chunks = []for (let i = 0; i < chunkNum; i++) {const start = i * chunkSizeconst end = Math.min(start + chunkSize, fileSize)chunks.push({index: i,start,end,downloaded: false,filePath: `${tempFilePath}${i}`})}// 进度状态:已下载的字节数let loadedBytes = 0// 下载分片const downloadChunk = (chunk) => {return new Promise((resolve, reject) => {const downloadTask = wx.downloadFile({url: downloadUrl,header: {},filePath: chunk.filePath,success: (res) => {if (res.statusCode === 200) {loadedBytes += chunk.end - chunk.start + 1chunk.downloaded = trueresolve()} else {reject(new Error('Download chunk error.'))}},fail: () => {reject(new Error('Download chunk fail.'))}})downloadTask.onProgressUpdate((res) => {const progress = Math.floor((loadedBytes + res.totalBytesWritten) / fileSize * 100)console.log('分片下载进度:', progress)this.setData({ progress })})this.setData({ downloadTask })})}Promise.all(chunks.map(downloadChunk)).then(() => {this.mergeFiles()}).catch((err) => {console.log(err)this.resetState()})},// 合并已下载的分片文件mergeFiles() {const fs = wx.getFileSystemManager()const stream = fs.createWriteStream(`${wx.env.USER_DATA_PATH}/downloads/${fileName}`)const write = (index) => {const chunk = chunks[index]if (!chunk.downloaded) {console.log(`Chunk ${chunk.index} not downloaded.`)return}fs.readFile(chunk.filePath, 'binary', (err, data) => {if (err) {console.log(err)return}stream.write(data, 'binary')if (index < chunks.length - 1) {write(index + 1)} else {// 文件合并完成stream.end()this.resetState()}})}write(0)},// 重置下载状态resetState() {this.setData({progress: 0,isDownloading: false,downloadTask: null})}
})

在 H5 页面中可以引入此文件作为下载操作的逻辑处理。同时还需要在 wxml 文件中添加下载按钮及进度条,代码如下:

<!-- download.wxml --><view class="download-container"><button class="download-btn" bindtap="startDownload">下载</button><progress percent="{{ progress }}%" />
</view>

需要在小程序的 app.json 文件中添加下载临时路径配置,代码如下:

抱歉,似乎代码显示出现了截断,请继续查看完整的 app.json 中的配置:

{"pages": ["pages/index/index","pages/download/download"],"permission": {"scope.userLocation": {"desc": "小程序将获取您的定位信息"}},"downloadFile": {"domain": [ "example.com" ],"timeout": 10000},"subpackages": []
}

其中,downloadFile 字段下对应了下载文件的配置,包括限定下载域名、超时时间等。文件下载的域名必须在此处进行配置,否则会被微信限制,无法正常下载。

总结:

大致过程就是这样,网上有很多解决方案都是在uni-app小程序的loading页面直接去缓存一下h5发过来的下载地址之后,直接返回到web-view页面去进行下载预览,他的 缺点点击下载时页面会一闪而过空白的微信页面再回来,我把下载直接放到了downLoading页面就是为了可以告诉用户我已经在下载了,可以给用户一点好的体验。

uni.navigateBack({delta: 1}); 返回到web-view的目的是为了你在预览界面点击左上角返回时可以回到web-view页面,直接下载预览都在downLoading页面的话,你在预览页面点击返回就会回到downLoading页面,体验不是很好。

实现微信小程序web-view内嵌H5中的下载功能(大文件切片下载)相关推荐

  1. 微信小程序,实现内嵌网页的分享

    自从微信小程序支持内嵌网页之后,呼声高涨得不得了.的确,这个确实让我开发我们公司的小程序高效了很多,主要是可以引入现有的功能完整的普通网页. 需求是这样子的:小程序启动授权等操作成功后直接跳转到内嵌网 ...

  2. 在微信小程序里面如何内嵌H5页面(web-view)

    不知道大家有没有注意到,从微信小程序基础库1.6.4开始,小程序内部新增了开放组件web-view,这个组件提供了类似于html页面的iframe功能,方便大家在微信小程序里面打开一下新的H5页面,在 ...

  3. 控制微信小程序web view的返回按钮

    需求:点击web-view页面的左上角返回按钮时,显示弹窗,点击取消或确认后才能返回.(实际上点击返回按钮会直接返回上一页) 解决方法:由于webview使用的是微信浏览器打开的,我们可以使用js的H ...

  4. 微信小程序学习——view的显示与隐藏

    微信小程序学习--view的显示与隐藏 需要在全局数据块中,设定一个控制键. data: {......//省略其他代码showView: true}, 然后是在wxml中,view的class中设置 ...

  5. 微信小程序跳转到第三方H5网页

    我开发过程中有小程序跳转到网页的需求,下面分享一下我的实现过程: 使用官方文档web-view组件:web-view 1.首先得通过微信公众平台配置业务域名,配置业务域名需要将校验文件上传到网站服务器 ...

  6. android img标签属性_微信小程序 组件叠加效果(如 Android 中的添加蒙层)

    实现的效果如下: 可以看出这是由image组件和text组件叠加到一块组成的蒙层效果. 在小程序中实现这个效果主要用到z-index属性和position属性 z-index的使用必须是双方组件都设置 ...

  7. 微信小程序(购物车)--在wxml中设置保留小数位数

    微信小程序(购物车)–在wxml中设置保留小数位数 一.在该页面文件夹下新建一个wxs后缀的文件 var filters = {toFix: function (value) {return valu ...

  8. 微信小程序:未找到 app.json 中的定义的 pages “pages/index/index“ 对应的 WXML 文件

    微信小程序:未找到 app.json 中的定义的 pages "pages/index/index" 对应的 WXML 文件 前情:本人在自学微信小程序时,遇到了调用模板出现错误的 ...

  9. 微信小程序使用echarts图表,与延迟加载图表,解决echarts文件过大

    微信小程序使用echarts图表,与延迟加载图表,解决echarts文件过大 引入echarts组件 展示echarts图表数据 延迟加载图表 如何解决echarts文件过大 引入echarts组件 ...

最新文章

  1. 【swjtu】数据结构实验4_基于改进KMP算法的子串查找与替换
  2. 替代触发器如何加判断 条件_《小逻辑》:如何设定目标,制定计划,做出更好的选择...
  3. python小数据池,代码块的最详细、深入剖析
  4. neo4j-admin导入海量数据
  5. Atom工具总结笔记
  6. 基于JAVA+Servlet+JSP+MYSQL的在线鲜花商城系统
  7. React 性能优化技巧总结
  8. 苹果手机上网很慢_手机信号满格,但上网速度却很慢?来听听通信专家怎么说的...
  9. 单片机IO详解(上拉 下拉 准双向 输入 输出 推挽 开漏)
  10. 【DockerCE】Docker-CE 20.10.17正式版发布
  11. VOA 2011-2-16
  12. 我是歌手黄绮珊(黄妈)彭佳慧,_bin910729_新浪博客
  13. 影响SAR图像电磁散射特性的因素
  14. 计算机网络验证性试验报告
  15. 几何畸变的类型_halcon图像畸变 论述遥感数字图像畸变类型及其产生的原因
  16. word中装订线位置_word装订线位置在哪里
  17. 汉文博士——支持生僻古难字检索的开放式免费汉语词典
  18. 使用JavaWeb实现多多自走棋羁绊生成
  19. 苹果手机打字换行怎么换_苹果id怎么换
  20. openwrt sdk下交叉编译aria2c

热门文章

  1. Qt中 gui 模块和 widgets 模块的区别
  2. 猿创征文|国产数据库[StarRocks]实战学习
  3. Android App数据加密
  4. PhotoSwipe.js 相册展示插件学习
  5. 喜欢《花样少年少女》
  6. MySQL:日期和时间函数
  7. Gulp项目报错:AssertionError [ERR_ASSERTION]: Task function must be specified
  8. 所谓框架到底是什么?
  9. 域格4G模块TTS语音使用方法
  10. excel单元格斜杠拆分上下打字