实现微信小程序内嵌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端,下载用一个标签就很容易,但是在小程序里的标签中是行不通的。此外,小程序里的与小程序的通行方式主要用postMessage,但是触发条件非常苛刻,参照微信的官方文档,只在小程序后退,组件销毁,还有分享时才会触发postMessage并一次性把值全部带出来,使用起来非常不便,对于小程序实际内容主体是内嵌的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页面

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

二、注意(下载大文件)

uni.downloadFile 主要包含两个参数:url 和 filePath,其中 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.js

const 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 * chunkSize

const 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 + 1

chunk.downloaded = true

resolve()

} 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 文件中添加下载按钮及进度条,代码如下:

需要在小程序的 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页面,体验不是很好。

参考链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: