谈谈小程序文件上传下载那些事~
- 2019 年 12 月 5 日
- 笔记
上传下载功能在日常开发时是一个很常见的功能,我们在app或者网站开发时,我们可以直接选择从本地打开不同格式的文件,然后通过form-data格式将图片提交到服务端并实现从上传操作。本篇文章主要讲讲小程序如何实现不同格式文件的上传及下载。在小程序中我们没办法像网站开发一样,使用input直接一个标签可以选择本地不同格式的文件,在小程序中,要选择不同格式的文件需要调用不同的API。
图片上传下载
说到图片上传,其实前两篇已经有涉及到这个概念。小程序选择本地图片是使用wx.chooseImage(Object object)这个API,我们先来看看文档的定义:

看文档我们可以很简单的学会使用这个API,调用API就会打开本地文件可以选择本地图片,选择成功会返回图片的临时路径,我们可以看下代码:
wx.chooseImage({ count: 1, // 默认9张图片 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success: function(res) { //获取到临时路径 var tempFilePaths = res.tempFilePaths; } });
在选择图片成功,返回的res会存在一个tempFilePaths数组,这里面实际上就是存放选择图片的临时路径,取到临时路径后我们就需要使用小程序封装好的wx.uploadFile(Object object)进行文件上传操作,我们先看下这个API的文档:

可以看到我们指定服务端文件上传接口的url,文件的临时路径以及设置文件的name值。如果我们还需要携带其他参数则可以在formData中指定其他参数。接下来我们看下具体代码:
wx.uploadFile({ url: utils.basePath + '/users/upload_avatar', filePath: tempFilePaths[0], name: 'avatar', headers: { 'Content-Type': 'form-data' }, success: function(res) { //服务端有正常响应,根据不用状态码实现不同逻辑 } });
一般来说如果进入success就代表文件成功提交到服务端了,一般上传成功或者失败返回不同状态码,然后客户端根据服务端返回的不同状态码执行不同的业务逻辑。文件上传成功后我们将服务端返回的图片的url地址写入到image标签的src属性就可以实现访问,然后我们就得考虑用户需要保存图片时如何操作了,在小程序用户需要保存图片一般有两种方式:预览图片长按保存或者调用API实现图片保存到本地相册。首先我们可以调用wx.prewiewImage(Object object)全屏预览图片,预览的时候我们就可以直接长按选择保存图片到本地。我们可以看看文档:

实际上我们只需要两个参数:指定当前预览图片url以及预览的图片链接列表。我们可以看下代码:
var res = e.target.dataset.src; var list = this.data.previewImgList; if (list.indexOf(res) == -1) { this.data.previewImgList.push(res); } wx.previewImage({ current: res, urls: that.data.previewImgList });
那第二种方式就是使用wx.saveImageToPhotosAlbum(Object object)将图片保存到本地相册。我们先看下文档:

可以看到只需要传入文件路径,但是后面备注写着不支持网络图片路径,那我们如何进行将图片下载到本地相册呢?我们就得额外加1步操作:先使用wx.downloadFile()先将网络文件下载得到临时路径,再调用wx.saveImageToPhotosAlbum(Object object)将图片保存到本地相册。我们可以看下代码:
//先下载文件获得临时路径 wx.downloadFile({ url: url, success: function (res) { wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success(res) { $Toast({ content: '视频保存成功', type: 'success' }); }, fail(err) { $Toast({ content: '视频保存失败!', type: 'error' }); } }); } });
使用先下载获取临时路径再保存到本地相册也一样可以实现图片保存到本地。当然我们更建议使用预览长按保存的方式,但是当业务出现需要点击按钮保存图片时我们就可以使用第二种方式去实现。到这里图片的上传下载成功实现,下一步我们来谈谈视频的上传下载。
视频上传下载
视频的选择和上传实际上和图片类似,我们选择视频需要使用到另一个API:wx.chooseVideo(Object object)来选择视频,选择完视频一样会返回一个临时链接tempFilePath,然后通过wx.uploadFile(Object object)上传视频到服务器。我们可以看下选择视频API的文档:

我们只需要指定选择视频来源,选择拍摄的最长时间等参数就可以调用该API。我们可以看看具体代码:
wx.chooseVideo({ sourceType: ['album', 'camera'], maxDuration: 60, camera: 'back', success: function(res) { var tempFilePaths = res.tempFilePath; wx.uploadFile({ url: utils.basePath + '/users/upload_video', filePath: tempFilePaths, name: 'mp4_url', headers: { 'Content-Type': 'form-data' }, success: function(res) { if (res.statusCode == 413) { $Toast({ content: '视频过大,请重新上传', type: 'error' }); } else { //上传视频成功进行业务逻辑 } } }); } });
上传视频这里一般要注意一个细节:一般服务器会有默认上传文件最大字节数的限制,比如Nginx如果没有指定默认最大上传文件限制在1M以内,所以我们需要通过在Nginx配置client_max_body_size参数设置为25M,然后因为文件过大服务器会返回413状态码,所以需要同时在wx.uploadFile时对statusCode为413的情况做一下限制即可实现视频上传。说完了视频上传功能,我们来讲讲视频下载到本地的功能,小程序实现视频下载到本地只有一种方式:先通过wx.downloadFile(Object object)先将网络文件下载得到临时路径,再调用wx.saveVideoToPhotosAlbum(Object object)将图片保存到本地相册。因为这个过程和图片下载的第二种方式雷同,所以直接贴一下代码:
//先下载文件获得临时路径 wx.downloadFile({ url: content, success: function (res) { wx.saveVideoToPhotosAlbum({ filePath: res.tempFilePath, success(res) { $Toast({ content: '视频保存成功', type: 'success' }); }, fail(err) { $Toast({ content: '视频保存失败!', type: 'error' }); } }); } });
其他文件格式上传与下载
图片与视频的上传下载有时候并不能符合我们的需求,比如我想发一个文档给好友,这时候上面的两种方式肯定不适用。那有没有一个API可以实现从本地选取文件上传服务器呢?答案是没有!但是微信有提供一个API可以从微信会话选取历史文件发送,这是什么意思呢?就是我们在微信客户端发送文件给好友或群,然后在小程序使用wx.chooseMessageFile(Object object)就可以打开微信好友界面,选择其中一个好友或群,就可以看到所有发送过的文件列表,我们选择文件后一样会得到一个临时链接,再次调用wx.uploadFile(Object object)就可以实现文件上传到服务器。我们可以看下wx.chooseMessageFile(Object object)的文档:

我们只需要指定文件个数,文件类型等参数就可以获得文件的临时链接和大小名称等参数。然后将文件上传服务器得到永久访问链接。我们来看看代码:
wx.chooseMessageFile({ count: 1, type: 'all', success(res) { var tempFiles = res.tempFiles[0]; wx.uploadFile({ url: utils.basePath + '/users/upload_video', filePath: tempFiles.path, name: 'mp4_url', headers: { 'Content-Type': 'form-data' }, success: function (res) { if (res.statusCode == 413) { $Toast({ content: '视频过大,请重新上传', type: 'error' }); } else { var result = JSON.parse(res.data); var chatInfo = that.data.chatInfo; //获得文件名称,路径以及大小 chatInfo.chat_content = JSON.stringify({ name: tempFiles.name, path: result.payload, size: tempFiles.size }); } } }); } });
一般来说我们如果上传文档的话有两个操作:打开文档浏览或者将文档下载到本地。我们可以先看看如何打开文档。小程序有提供wx.openDocument(Object object)来另开新界面打开文档,我们可以看看API文档:

可以看到一样只支持临时路径,需要先doloadFile一下得到临时路径,再调用API打开文档,而我们这个API支持常用的所有文档类型,我们可以看下API支持的类型:

话不多说我们直接看看代码:
wx.downloadFile({ url: e.currentTarget.dataset.path, success: function (res) { wx.openDocument({ filePath: res.tempFilePath, success: function (res) { console.log('打开文档成功'); } }); } });
那我们如果需要保存文件到本地要如何实现呢?这时候就只能调用wx.saveFile(Object object)API了。直接看下文档:

可以看到一样要求是文件的临时路径,所以还是需要先download一下得到临时路径再调用API进行保存。我们可以看看代码:
wx.downloadFile({ url: e.currentTarget.dataset.path, success: function (res) { wx.saveFile({ tempFilePath: res.tempFilePath, success(res) { const savedFilePath = res.savedFilePath; } }); } });
这里有一点需要注意的是:我们download得到的临时路径是可以在小程序直接使用的,但是经过saveFile操作后临时路径就直接失效,这时候如果我们逻辑中是直接使用的临时路径的话就会出问题,所以我们需要将saveFile返回的saveFilePath替换我们保存的临时路径才可以正常访问。当然更直接的方案应该是所有文件访问都使用文件上传成功服务端返回的url进行访问。而且这个下载后的文件实际上只支持在小程序中使用,也就是说离开小程序我们是无法查找到这个文件的保存位置的。所以这个API的使用场景其实不广。有关于在小程序中的文件上传下载基本就涵盖这么多内容了,下一篇将从零开始封装服务端文件上传,将在Node上传文件(1)的基础上继续改进以支持所有格式的文件上传。
目前博客小程序前后端已开源于码云,欢迎来一个star。源码地址:
https://gitee.com/mqzuimeng_admin/wx_blog.git