談談小程序文件上傳下載那些事~
- 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


