news 2026/3/20 23:02:29

微信小程序下载超过200MB的文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序下载超过200MB的文件

查看微信的官方文档

DownloadTask wx.downloadFile(Object object)

以 Promise 风格 调用:不支持

小程序插件:支持,需要小程序基础库版本不低于 1.9.6

微信 Windows 版:支持

微信 Mac 版:支持

微信 鸿蒙 OS 版:支持

相关文档: 网络使用说明、局域网通信

功能描述

下载文件资源到本地。客户端直接发起一个 HTTPS GET 请求,返回文件的本地临时路径 (本地路径),单次下载允许的最大文件为 200MB。使用前请注意阅读相关说明。

注意:请在服务端响应的 header 中指定合理的Content-Type字段,以保证客户端正确处理文件类型。

参数

Object object

属性类型默认值必填说明最低版本
urlstring下载资源的 url
headerObjectHTTP 请求的 Header,Header 中不能设置 Referer
timeoutnumber60000超时时间,单位为毫秒,默认值为 60000 即一分钟。2.10.0
filePathstring指定文件下载后存储的路径 (本地路径)1.8.0

文档标识只支持200MB的文件,那么我们如何下载超过200MB的文件呢

我尝试通过分块下载来实现这个逻辑,先把文件拆分成多个子文件下载下来,然后再一起合并成最终的视频。

import {authorize} from "./download"; const fs = wx.getFileSystemManager(); const fileName = 'video.mp4' // 下载的文件名 const tempFilePath = wx.env.USER_DATA_PATH + '/downloads4_' // 临时文件存储路径 // const FILE_PATH = `${wx.env.USER_DATA_PATH}/downloaded_video.mp4`; // 最终合并输出路径 const BLOCK_SIZE = 1024 * 1024 * 300; // 每块大小:5MB /** * 获取远程视频文件大小 */ function getFileSize(url) { return new Promise((resolve, reject) => { uni.request({ url, method: 'HEAD', success: (res) => { const size = parseInt(res.header['Content-Length'] || res.header['content-length']); resolve(size); }, fail: reject, }); }); } /** * 下载单个分块并写入临时 chunk 文件 */ function downloadChunk(url, start, end, index,callback) { return new Promise((resolve, reject) => { // uni.request({ // url, // header: { // Range: `bytes=${start}-${end}`, // }, // responseType: 'arraybuffer', // success: (res) => { // const chunkPath = `${wx.env.USER_DATA_PATH}/chunk_${index}`; // fs.writeFile({ // filePath: chunkPath, // data: res.data, // encoding: 'binary', // success: resolve, // fail: reject, // }); // }, // fail: reject, // }); authorize().then(() => { console.log(`${tempFilePath}${index}`) const tmpFile = wx.env.USER_DATA_PATH + '/downloads4_'+index+".mp4" const task = wx.downloadFile({ url: `${url}?t=${new Date().getTime()}`, //仅为示例,并非真实的资源 // header: { // Range: `bytes=${start}-${end}`, // }, // filePath:tmpFile, // filepath: tmpFile, success: (res) => { console.log(res); if (res.statusCode >= 200 && res.statusCode < 300) { wx.saveVideoToPhotosAlbum({ filePath: res.tempFilePath, success:function(res) { console.log(res) resolve(); // wx.hideLoading(); }, fail: (err) => { console.log(err) reject(new Error('Download chunk fail.')) } }) } else { reject(new Error('Download chunk error.')) } }, fail: (err) => { console.log(err) reject(new Error('Download chunk fail.')) } }) task?.onProgressUpdate((res) => { const { progress, totalBytesWritten, totalBytesExpectedToWrite } = res console.log('下载进度' + progress); console.log('已经下载的数据长度' + totalBytesWritten); console.log('预期需要下载的数据总长度' + totalBytesExpectedToWrite); if (callback) { callback(res); }else{ uni.showToast({ icon: "none", title: `下载进度: ${progress}%`, }); } if (totalBytesExpectedToWrite > MaxTotalSize) { const CurTotalSizeMB = (totalBytesExpectedToWrite / 1024 / 1024).toFixed(2); // FailReason.errMsg = `当前文件${CurTotalSizeMB}MB,小程序单次下载允许的最大文件仅为${MaxTotalSizeMB}MB。您可复制下载链接,切换到本地浏览器自行下载!`; task.abort(); return; } }) }) }); } /** * 合并所有 chunk 成一个完整文件 */ function mergeChunks(chunkCount, targetFilePath) { return new Promise((resolve, reject) => { try { // 清空或创建目标文件 fs.writeFileSync(targetFilePath,'', 'binary' ); for (let i = 0; i < chunkCount; i++) { // const chunkPath = `${wx.env.USER_DATA_PATH}/chunk_${i}`; const chunkPath = wx.env.USER_DATA_PATH + '/downloads4_'+i+".mp4"; console.log("chunkPath:"+chunkPath); const chunkData = fs.readFileSync(chunkPath, 'binary'); fs.appendFileSync( targetFilePath, chunkData, 'binary', ); // 删除临时 chunk fs.unlinkSync(chunkPath); } resolve(); } catch (err) { console.log(err) reject(err); } }); } // 合并已下载的分片文件 function mergeFiles(chunkCount) { const fs = wx.getFileSystemManager() const stream = fs.createWriteStream(`${wx.env.USER_DATA_PATH}/downloads4_${fileName}`) const write = (index) => { console.log("开始合并"+index); const path = wx.env.USER_DATA_PATH + '/downloads4_'+index+".mp4"; // const path = `${wx.env.USER_DATA_PATH}/chunk_${index}`; // const chunk = chunks[index] // if (!chunk.downloaded) { // console.log(`Chunk ${chunk.index} not downloaded.`) // return // } fs.readFile(path, 'binary', (err, data) => { if (err) { console.log(err) return } stream.write(data, 'binary') if (index < chunkCount - 1) { write(index + 1) } else { // 文件合并完成 stream.end() // this.resetState() } }) } write(0) } /** * 下载完整视频,带进度回调 */ async function downloadVideoWithChunks(url, callback) { const totalSize = await getFileSize(url); console.log("totalSize:"+totalSize); const chunkCount = Math.ceil(totalSize / BLOCK_SIZE); for (let i = 0; i < chunkCount; i++) { const start = i * BLOCK_SIZE; const end = Math.min(start + BLOCK_SIZE - 1, totalSize - 1); await downloadChunk(url, start, end, i,callback); // if (onProgress) { // onProgress(i + 1, chunkCount); // } } const fullPath = `${wx.env.USER_DATA_PATH}/downloads4_${fileName}` console.log('fullPath', fullPath) // await mergeChunks(chunkCount,fullPath); // await mergeFiles(chunkCount); return fullPath; } /** * 保存文件到相册,带权限检查 */ function requestPermissionAndSave(filePath) { return new Promise((resolve, reject) => { uni.getSetting({ success(settingRes) { if (!settingRes.authSetting['scope.writePhotosAlbum']) { uni.authorize({ scope: 'scope.writePhotosAlbum', success: () => { uni.saveVideoToPhotosAlbum({ filePath, success: resolve, fail: reject, }); }, fail: () => { uni.showModal({ title: '提示', content: '请授权保存到相册', success(res) { if (res.confirm) wx.openSetting(); }, }); reject(new Error('未授权')); }, }); } else { uni.saveVideoToPhotosAlbum({ filePath, success: resolve, fail: reject, }); } }, fail: reject, }); }); } /** * 主函数:分块下载 + 合并 + 保存 */ export async function downloadAndSaveVideo(url, onProgress) { try { const path = await downloadVideoWithChunks(url, onProgress); // await requestPermissionAndSave(path); uni.showToast({ title: '保存成功', icon: 'success' }); } catch (err) { console.error('下载或保存失败:', err); uni.showToast({ title: '失败', icon: 'none' }); } }

但在分块的逻辑下,虽然下载子文件是没有问题,但是在最终合成大视频的环节中还是会报200mb限制大小的错误,所以这个方案还是不可行的。

最后的最后,我神奇的发现,其实wx.downloadFile 是支持超过200MB的文件,你不要自己定义filePath,你用它自己返回的文件路径就好

const Task = uni.downloadFile({

// url: url, //仅为示例,并非真实的资源

url: `${url}?t=${new Date().getTime()}`, //仅为示例,并非真实的资源

// filepath: filePath, // 之前我这里自定义了路径,这里需要注释掉

timeout: 300 * 1000, // 设置特定超时时间5分钟

success: (res) => {

你用它返回的res.tempFilePath 去保存 (要用真机调试,开发者工具做了200MB的拦截)

wx.saveVideoToPhotosAlbum({

filePath: res.tempFilePath,

success:function(res) {

resolve();

// wx.hideLoading();

},

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/15 9:32:52

拒绝死记硬背!我是如何通过“内存引用图”彻底终结闭包困惑的?

拨开迷雾&#xff1a;一次深入 JavaScript 闭包与内存模型的探索之旅 引言 JavaScript 中的闭包&#xff08;Closure&#xff09;是一个老生常谈的话题&#xff0c;但真正能从底层内存机制上将其彻底讲透的人并不多。在很长一段时间里&#xff0c;我对闭包的理解停留在“函数记…

作者头像 李华
网站建设 2026/3/14 13:10:08

高校科研首选工具:EmotiVoice助力语音AI教学

高校科研首选工具&#xff1a;EmotiVoice助力语音AI教学 在人工智能课程的实验课上&#xff0c;一名学生正尝试为一段物理讲义生成配音。他上传了5秒自己朗读的样本音频&#xff0c;输入文本“牛顿第一定律指出&#xff0c;物体在不受外力作用时将保持静止或匀速直线运动”&…

作者头像 李华
网站建设 2026/3/13 7:28:12

情感语音合成标准建立中,EmotiVoice参与制定

情感语音合成标准建立中&#xff0c;EmotiVoice参与制定 在虚拟偶像直播时突然“变脸”惊喜祝福观众&#xff0c;或是有声读物中的角色因剧情转折而声音颤抖——这些不再只是影视特效&#xff0c;而是正在走进现实的智能语音新体验。随着人机交互从“能听懂”迈向“懂情绪”&am…

作者头像 李华
网站建设 2026/3/13 6:19:12

10、深入解析 Samba 服务器配置:从基础到高级设置

深入解析 Samba 服务器配置:从基础到高级设置 1. Samba 用户与密码管理 Samba 提供了两个重要的密码管理子菜单,分别用于管理本地和远程服务器的用户与密码。 - 服务器密码管理子菜单 :可对与本地计算机关联的 Samba 用户进行管理,包括添加、删除、禁用、启用用户,以…

作者头像 李华
网站建设 2026/3/18 5:33:21

25、Red Hat Linux系统管理与备份全攻略

Red Hat Linux系统管理与备份全攻略 1. Red Hat Linux救援模式 当在计算机上启动Red Hat Linux遇到问题时,可以使用安装过程中创建的引导软盘。即便在安装Red Hat Linux之后,也能创建该引导软盘。例如,若看到如下提示信息: Booting Red Hat Linux (2.4.20-9) root(hd1,…

作者头像 李华