云開發(fā) 圖片、緩存與文件

2020-07-20 11:21 更新

在前面的章節(jié),我們非常強調(diào)JavaScript對數(shù)據(jù)的操作,這一節(jié)我們來了解一下小程序與客戶端(手機)更深的交互。前面章節(jié)將數(shù)據(jù)存儲到通訊錄(添加手機聯(lián)系人)、存儲到剪切板(用手機復(fù)制粘貼),小程序就已經(jīng)與客戶端手機有了交互,這一節(jié)我們將來獲取手機相冊里的圖片和手機相機拍照的照片、手機的定位以及獲取手機里的緩存、文件,并使用JavaScript操作圖片、操作緩存和操作文件等。

獲取手機相冊或拍照的圖片

用小程序來獲取手機相冊里的圖片和拍照的照片聽起來好像挺復(fù)雜的,不過因為有了API,我們只需要結(jié)合前面的點擊事件、事件處理函數(shù)以及調(diào)用API、傳入指定的參數(shù)就能很容易做到。

技術(shù)文檔:wx.chooseImage()

上傳一張照片

使用開發(fā)者工具新建一個file的頁面,然后在file.wxml里輸入以下代碼:

<button bindtap="chooseImg">選擇圖片</button>
<image mode="widthFix" src="{{imgurl}}"></image>
<view>上傳的圖片</view>

然后在file.js的data里給imgurl設(shè)置一個初始值,由于鏈接src是一個字符串類型,我們這里可以設(shè)置為一個字符串空值,完成imgurl的初始化:

  data: {
    imgurl:"",
  },

再在file.js里添加事件處理函數(shù)chooseImg,在chooseImg里我們來調(diào)用上傳函數(shù)的API wx.chooseImage(),其中count、sizeType、sourceType都是API已經(jīng)寫好的屬性,API調(diào)用成功(圖片上傳成功)之后,會在success回調(diào)函數(shù)里返回圖片的一些信息,返回的信息可以看技術(shù)文檔。

  chooseImg:function(){
    let that=this
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const imgurl = res.tempFilePaths
        that.setData({
          imgurl
        })
      }
    })
  },

雖然在開發(fā)者工具的模擬器也可以看到效果,但是wx.chooseImage()是一個與手機客戶端交互性很強的API,我們最好在手機上體驗。點擊開發(fā)者工具的預(yù)覽,在手機微信里查看效果,點擊選擇圖片按鈕,上傳一張圖片或拍照看看。

  • count:可以選擇的照片數(shù)量,默認為9張(由于imgurl聲明的是字符串,多張照片需為數(shù)組Array,后面有上傳多張圖片的案例)
  • sourceType:選擇圖片的來源,album就是圖片可以來自手機相冊;而camera是可以來自手機拍照,兩個都寫就是來自相冊或拍照都可以;
  • sizeType:所選的圖片的尺寸,original為原圖,compressed為壓縮圖,為了減輕服務(wù)器壓力,建議為壓縮圖;
  • tempFilePaths為臨時文件的路徑列表,tempFiles為臨時文件列表,注意這兩個值都為數(shù)組。

    小任務(wù):將sourceType的值修改為 ['album'],在手機微信上看看有什么效果?再將sizeType改為 ['compressed'],看手機是否還能夠上傳原圖?

空值的處理

我們可以看到由于imgurl為空值,image組件有默認寬度300px、高度225px(會隨css而改變大?。?,所以顯示上傳的圖片會與選擇圖片的button有一段空白,處理的方法有三種:

方法一:我們可以給imgurl弄一張初始圖片的鏈接,為了讓界面更加美觀、交互性更好,通常都會設(shè)置一個默認的圖片,比如默認的頭像,當(dāng)用戶上傳時,setData就會取代初始圖片;

方法二:判斷imgurl是否有內(nèi)容,比如我們可以加一層邏輯判斷,當(dāng)Page()里的data下的imgurl屬性非空時,組件才會顯示;空時就不顯示。

<view wx:if="{{!!imgurl}}">
    <image mode="widthFix" src="{{imgurl}}"></image>
</view>

方法三:這個方法和方法二類似,設(shè)置一個邏輯判斷,比如在data里設(shè)置一個boolean屬性比如hasImg,初始值為false,

  data: {
    hasImg:false,
  },

當(dāng)chooseImg回調(diào)成功之后,在that.setData里把hasImg修改為true,也就是將wx.chooseImage()的success回調(diào)函數(shù)里的that.setData()修改為:

that.setData({
  imgurl,
  hasImg:true,
})

這樣是否有圖片就進入到了回調(diào)函數(shù)的邏輯里了,接著我們把file.wxml的代碼改為如下:

<view wx:if="{{hasImg === false}}">
  <button bindtap="chooseImg">選擇圖片</button>
</view>
<view wx:if="{{hasImg === true}}">
    <image mode="widthFix" src="{{imgurl}}"></image>
</view>

沒有圖片也就是hasImg的值為false時,會顯示選擇圖片的button;而當(dāng)有圖片時,沒有button只有圖片,在一定的場合用戶體驗會更好(button要是一直在,用戶就還會去點,體驗不好)。

注意:這里所說的上傳圖片與我們?nèi)粘I钪械纳蟼鲌D片不是一樣的哦,日常生活中上傳圖片,圖片不僅會顯示在小程序(網(wǎng)頁、App)上,還會繼續(xù)上傳到存儲服務(wù)器里面,而我們這里只是進行了第一步,上傳的圖片只是存儲在臨時文件里面,所以重新編譯,圖片就不顯示了。后面會有臨時文件的內(nèi)容以及會在云開發(fā)部分將圖片上傳到云存儲。

上傳多張照片

如果上傳的是多張照片,那么imgurl的初始值就不能是字符串了,而是一個數(shù)組Array,

  data: {
    imgurl:[],
  },

而file.wxml的代碼也要相應(yīng)的改為列表渲染即可,這種寫法在代碼上通用性比較強,上傳一張圖片、多張圖片都可以,不過具體還是要看實際產(chǎn)品開發(fā)需求。

<view wx:for-items="{{imgurl}}" wx:for-item="item" wx:key="*this">
  <image mode="widthFix" src="{{item}}"></image>
</view>

然后再把count的值修改為2~9張,編譯之后,在手機微信上體驗一下效果。

操作圖片

使用小程序圖片API不僅可以上傳圖片,還可以對上傳的圖片進行一定的操作,比如獲取圖片信息、預(yù)覽圖片、保存圖片、壓縮圖片等等。

獲取圖片信息

無論是存儲在小程序本地,還是存儲在臨時文件、緩存、網(wǎng)絡(luò)上的圖片,使用wx.getImageInfo() 都可以獲取到該圖片的寬度、高度、路徑、格式以及拍照方向。

技術(shù)文檔:wx.getImageInfo()

使用開發(fā)者工具在file.js里添加以下代碼,我們使用wx.getImageInfo() 來獲取之前上傳的圖片的信息。由于獲取圖片信息需要等上傳圖片成功之后才能執(zhí)行,因此我們可以在wx.chooseImage()的success回調(diào)函數(shù)里來調(diào)用wx.getImageInfo(),而獲取圖片信息之后才能返回圖片信息,因此這又是一個回調(diào)函數(shù):

  chooseImg:function(){
    let that=this
    wx.chooseImage({
      count: 9,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success(res) {
        const imgurl = res.tempFilePaths
        console.log('chooseImage回調(diào)打印的res',res)
        that.setData({
          imgurl
        })
        wx.getImageInfo({
          src: res.tempFilePaths[0],
          //也可以這么寫:src: that.data.imgurl[0],這里只能看到第一張照片的信息,其他照片的信息需要遍歷來獲取
          success(res){
            console.log('getImageInfo回調(diào)打印的res',res)
          }
        })
      }
    })
  },

編譯之后,我們再來上傳一張圖片,圖片上傳成功之后,在控制臺console里可以看到打印的信息。在上面的代碼里,我們發(fā)現(xiàn)success回調(diào)函數(shù)嵌套success回調(diào)函數(shù)。

回調(diào)函數(shù)


經(jīng)過之前的學(xué)習(xí),相信大家對回調(diào)函數(shù)success、fail有了一定的認識,那什么是回調(diào)函數(shù)呢?簡單一點說就是:回調(diào)Callback是指在另一個函數(shù)執(zhí)行完成之后被調(diào)用的函數(shù)。success、fail就都是在小程序的API函數(shù)執(zhí)行完成之后,才會被調(diào)用,而success和fail它們本身也是函數(shù),也能返回數(shù)據(jù)。而復(fù)雜一點說,就是回調(diào)函數(shù)本身就是函數(shù),但是它們卻被其他函數(shù)給調(diào)用,而調(diào)用函數(shù)的函數(shù)被稱為高階函數(shù)。這些大家只需要粗略了解就可以了。


異步與同步


我們前面也提及過異步,那什么會有異步呢?因為JavaScript是單線程的編程語言,就是從上到下、一行一行去執(zhí)行代碼,類似于排隊一樣一個個處理,第一個不處理完,就不會處理后面的。但是遇到網(wǎng)絡(luò)請求、I/O操作(比如上面的讀取圖片信息)以及定時函數(shù)(后面會涉及)以及類似于成功反饋的情況,等這些不可預(yù)知時間的任務(wù)都執(zhí)行完再處理后面的任務(wù),肯定不行,于是就有了異步處理。


把要等待其他函數(shù)執(zhí)行完之后,才能執(zhí)行的函數(shù)(比如讀取圖片信息)放到回調(diào)函數(shù)里,先不處理,等圖片上傳成功之后再來處理,這就是異步。比如wx.showToast()消息提示框,可以放到回調(diào)函數(shù)里,當(dāng)API調(diào)用成功之后再來顯示提示消息。回調(diào)函數(shù)相當(dāng)于是異步的一個解決方案。

預(yù)覽所有上傳的圖片

預(yù)覽圖片就是在新頁面里全屏打開圖片,預(yù)覽的過程中用戶可以進行保存圖片、發(fā)送給朋友等操作??梢灶A(yù)覽一張照片或者多張照片。

技術(shù)文檔:wx.previewImage()

使用開發(fā)者工具在file.wxml里輸入以下代碼,我們要預(yù)覽的是從手機相冊里上傳的圖片(保留上面的代碼,接著寫),如果沒有上傳圖片,那就把預(yù)覽圖片的按鈕給隱藏,我們來寫一段完整的代碼:

<view wx:if="{{hasImg === true}}">
    <button bindtap="previewImg">預(yù)覽照片</button>
</view>

然后在file.js添加事件處理函數(shù)previewImg,調(diào)用預(yù)覽圖片的API wx.previewImage():

  previewImg:function(){
    wx.previewImage({
      current: '',
      urls: this.data.imgurl,
    })
  },

當(dāng)上傳圖片之后點擊預(yù)覽圖片按鈕就能預(yù)覽所有圖片了。

這個場景主要用于讓用戶可以預(yù)覽、保存或分享圖片,畢竟image組件是不支持圖片的放大預(yù)覽、保存到本地、轉(zhuǎn)發(fā)給好友,現(xiàn)在微信還支持預(yù)覽小程序碼,長按就可以打開小程序,這個API主要是為了增強用戶的交互體驗的。

那我們應(yīng)該如何實現(xiàn)點擊其中的某一張圖片,就會彈出所有圖片的預(yù)覽呢?這里就要用到current了。

將之前file.wxml里圖片上傳的代碼改成如下,把事件處理函數(shù)previewImg綁定在圖片上面,

<button bindtap="chooseImg">選擇圖片</button>
<view wx:for-items="{{imgurl}}" wx:for-item="item" wx:key="*this">
  <image mode="widthFix" src="{{item}}" data-src="{{item}} " bindtap="previewImg" style="width:100px;float:left"></image>
</view>

然后將file.js的事件處理函數(shù)previewImg修改為:

  previewImg:function(e){
    wx.previewImage({
      current: e.currentTarget.dataset.src,
      urls: this.data.imgurl,
    })
  },

這樣點擊圖片就會彈出預(yù)覽窗口來預(yù)覽圖片了。

保存圖片到相冊

小程序不支持直接將網(wǎng)絡(luò)圖片保存到本地手機的系統(tǒng)相冊,支持臨時文件路徑和小程序本地路徑。

技術(shù)文檔:wx.saveImageToPhotosAlbum()

比如我們在小程序的根目錄下新建一個image文件夾并放一張圖片到里面比如background.jpg,然后再在file.wxml里輸入以下代碼,讓image組件綁定事件處理函數(shù)saveImg:

<image mode="widthFix" src="/images/background.jpg" bindtap="saveImg"></image>

然后在file.js里添加事件處理函數(shù)saveImg,

  saveImg:function(e){
    wx.saveImageToPhotosAlbum({
      filePath: "/images/background.jpg",
      success(res) { 
        wx.showToast({
          title: '保存成功',
        })
      }
    })
  }

編譯之后預(yù)覽在手機里體驗,點擊圖片就會觸發(fā)事件處理函數(shù)saveImg,調(diào)用wx.saveImageToPhotosAlbum() API,filePath為小程序文件的永久鏈接,文件就會保存到手機相冊(沒有相冊權(quán)限會提示)。

當(dāng)然永久鏈接實際開發(fā)用得不會太多,使用最多的場景是把網(wǎng)絡(luò)圖片下載到臨時鏈接(因為不能直接保存網(wǎng)絡(luò)圖片),再將臨時鏈接的圖片保存到相冊,只需把上面的永久鏈接換成臨時鏈接就可以了。最重要的是要搞清楚圖片到底在哪里,在網(wǎng)絡(luò)上?還是在小程序本地?還是在臨時文件里?還是在緩存里?

壓縮圖片

小程序是有壓縮圖片的API的wx.compressImage(),尤其是在上傳圖片時,為了減輕存儲服務(wù)器的壓力,不能讓用戶上傳分辨率過高的照片。

  • 可以先讓用戶上傳圖片;
  • 圖片上傳成功之后(也就是在上傳圖片的success回調(diào)函數(shù)里)再來獲取圖片的信息;
  • 獲取信息成功后(也就是在獲取圖片信息的success回調(diào)函數(shù)里)判斷寬度或高度是否過大,如果圖片過大,就壓縮圖片,
  • 壓縮圖片成功后(也就是在壓縮圖片的success回調(diào)函數(shù)里),再把壓縮好的圖片上傳到服務(wù)器

上傳圖片、獲取圖片信息、壓縮圖片、上傳圖片到服務(wù)器,每一步都依賴上一步,所以會不斷在success回調(diào)函數(shù)里寫函數(shù),實際開發(fā)涉及的業(yè)務(wù)會更復(fù)雜,就會不斷回調(diào),這被稱之為回調(diào)地獄。這就是為什么會有Promise寫法的原因,這個我們會在以后提及。

由于壓縮圖片使用到的場景不算太多,畢竟我們在上傳照片時可以不支持上傳原圖original,只支持壓縮compressed就能保證上傳圖片的大小了。而且wx.compressImage()壓縮圖片的API也比較簡單,所以這里就不寫實際案例了,相信大家看文檔也能玩得明白。

上傳文件

小程序不僅支持上傳圖片image,還支持上傳視頻、Excel、PDF、音頻等等其他文件格式,但是我們只能從客戶端會話里(也就是微信單聊、群聊的聊天記錄里)選擇其他格式的文件。

技術(shù)文檔:wx.chooseMessageFile()

使用開發(fā)者工具在file.wxml里添加以下代碼,給選擇文件的button綁定事件處理函數(shù)chooseFile:

<button bindtap="chooseFile">選擇文件</button>

在file.js文件里添加事件處理chooseFile,并打印上傳成功后回調(diào)函數(shù)里的參數(shù)對象。

  chooseFile: function () {
    let that = this
    wx.chooseMessageFile({
      count: 5,
      type: 'file',
      success(res) {
        console.log('上傳文件的回調(diào)函數(shù)返回值',res)
      }
    })
  },

使用開發(fā)者工具上傳一張圖片或其他格式的文件,在控制臺console我們可以看到打印的res對象里有tempFiles的數(shù)組對象Array(沒有tempFilePaths,此處技術(shù)文檔有誤),tempFiles對象包含文件的名稱name、文件的臨時路徑path、文件的大小size、選擇的文件的會話發(fā)送時間戳time、文件的類型type。

我們可以把上傳的文件所取得的信息給渲染到頁面上,在file.wxml里添加列表渲染的代碼,也就是

<button bindtap="chooseFile">選擇文件</button>
<view wx:for-items="{{tempFiles}}" wx:for-item="item" wx:key="*this">
  <view>{{item.path}}</view>
</view>

在Page()的data里初始化一個屬性temFiles,初始值為一個空數(shù)組Array:

  data: {
    tempFiles:[],
  },

然后再在chooseFile的success回調(diào)函數(shù)里將數(shù)據(jù)使用setData賦值給tempFiles:

  chooseFile: function () {
    let that = this
    wx.chooseMessageFile({
      count: 5,
      type: 'file',
      success(res) {
        let tempFiles=res.tempFiles
        that.setData({
          tempFiles
        })
      }
    })
  },

編譯之后預(yù)覽在微信上體驗,看看什么效果?注意需選擇微信有文件的會話框。還是再強調(diào)一下,這個上傳和我們實際里的上傳還是不一樣的,這里只是把文件上傳到了一個臨時文件里面,并沒有上傳到服務(wù)器。

上傳地理位置

除了可以上傳圖片、音視頻以及各種文件格式以外,小程序還支持上傳地理位置。

技術(shù)文檔:wx.chooseLocation()

使用開發(fā)者工具在file.wxml里輸入以下代碼,前面我們上傳了文件,這一次我們把手機的位置給上傳并渲染出來:

<button bindtap="chooseLocation">選擇地理位置</button>
<view>{{location.name}}</view>
<view>{{location.address}}</view>
<view>{{location.latitude}}</view>
<view>{{location.longitude}}</view>

然后在file.js的Page()的data里初始化location

  data: {
    location:{},
  },

在file.js里添加事件處理函數(shù)chooseLocation,

  chooseLocation: function () {
    let that= this
    wx.chooseLocation({
      success: function(res) {
        const location=res
        that.setData({
          location
        })
      },
      fail:function(res){
        console.log("獲取位置失敗")
      }
    })
  },

編譯之后預(yù)覽用手機點擊選擇地理位置的button,就會彈出地圖選擇位置(這個位置既可以是你當(dāng)前的位置,也可以自己選擇一個位置),然后點擊確定,就能在小程序上看到我們上傳的位置了。要讓位置信息顯示在地圖上,可以在file.wxml里添加一個地圖組件:

<map style="width: 100%; height: 300px;"
  latitude="{{location.latitude}}"
  longitude="{{location.longitude}}"
  show-location
></map>
小任務(wù):上傳地理位置,將該地址顯示在地圖上,并添加該地理位置的markers。關(guān)于markers的知識,可以去看map組件的技術(shù)文檔。

模塊化與格式化

在新建模板小程序里(不使用云開發(fā)服務(wù)),有一個日志logs頁面,這個日志logs雖然簡單,但是包含著非常復(fù)雜的JavaScript知識,是一個非常好的學(xué)習(xí)參考案例,這里我們來對它進行一一解讀。

模塊化與引入模塊

在實際開發(fā)中,日期、時間的處理經(jīng)常會使用到,但是使用Date對象所獲取到的時間格式與我們想要展現(xiàn)的形式是有非常大的差異的。這時我們可以把時間的處理抽離成為一個單獨的 js 文件比如util.js(util是utility的縮寫,表示程序集,通用程序等意思),作為一個模塊。

把通用的模塊放在util.js或者common.js,把util.js放在utils文件夾里等就跟把css放在style文件夾,把頁面放在pages文件夾,把圖片放在images文件夾里是一樣的道理,盡管文件夾或文件的名稱你可以任意修改,但是為了代碼的可讀性,文件結(jié)構(gòu)的清晰,推薦大家采用這種一看就懂的方式。

使用開發(fā)者工具在小程序根目錄新建一個utils文件夾,再在文件夾下新建util.js文件,在util.js里輸入以下代碼(也就是參考模板小程序的logs頁面調(diào)用的util.js)

const formatTime = date => {
  const year = date.getFullYear()  //獲取年
  const month = date.getMonth() + 1  //獲取月份,月份數(shù)值需加1
  const day = date.getDate()  //獲取一月中的某一天
  const hour = date.getHours() //獲取小時
  const minute = date.getMinutes()  //獲取分鐘
  const second = date.getSeconds() //獲取秒

 
  return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')  //會單獨來講解這段代碼的意思
}

 
const formatNumber = n => {  //格式化數(shù)字
  n = n.toString()
  return n[1] ? n : '0' + n 
}

 
module.exports = {  //模塊向外暴露的對象,使用require引用該模塊時可以獲取
  formatTime: formatTime,
  formatNumber: formatNumber
}

我們再來在file.js里調(diào)用這個模塊文件util.js,也就是在file.js的Page()對象前面使用require引入util.js文件(需要引入模塊文件相對于當(dāng)前文件的相對路徑,不支持絕對路徑)

const util = require('../../utils/util.js')

然后再在onLoad頁面生命周期函數(shù)里打印看看這段時間處理的代碼到底做了什么效果,這里也要注意調(diào)用模塊里的函數(shù)的方式。

  onLoad: function (options) {
    console.log('未格式化的時間',new Date())
    console.log('格式化后的時間',util.formatTime(new Date()))
    console.log('格式化后的數(shù)值',util.formatNumber(9))
  },

util.formatTime()就調(diào)用了模塊里的函數(shù),通過控制臺打印的日志可以看到日期時間格式的不同,比如: 未格式化的時間 Mon Sep 02 2019 11:25:18 GMT+0800 (中國標準時間) 格式化后的時間 2019/09/02 11:25:18

未格式化的時間 Mon Sep 02 2019 11:25:18 GMT+0800 (中國標準時間) 格式化后的時間 2019/09/02 11:25:18

顯然格式化后的日期時間的展現(xiàn)形式更符合我們的日常習(xí)慣,而9這個數(shù)值被轉(zhuǎn)化成了字符串”09″。那這段格式化日期時間的代碼是怎么實現(xiàn)的呢?這里就涉及到高階函數(shù)的知識,一般函數(shù)調(diào)用參數(shù),而高階函數(shù)會調(diào)用其他函數(shù),也就是把其他函數(shù)作為參數(shù)。

map

相信格式化數(shù)字的代碼比較好理解,如果是15日里的15,由于n[1]是15的第二位數(shù)字5,為true會直接return返回n,也就是15;比如9月里的數(shù)字9,n[1]不存在,也就是沒有第二位數(shù),于是執(zhí)行 '0' + n給它加一個0,變成09;而formatNumber是一個箭頭函數(shù)。

const formatNumber = n => {  //格式化數(shù)字
  n = n.toString() //將數(shù)值Number類型轉(zhuǎn)為字符串類型,不然不能拼接
  return n[1] ? n : '0' + n //三目運算符,如果字符串n第2位存在也就是為2位數(shù),那么直接返回n;如果不存在就給n前面加0
}

而格式化日期時間則涉及到map,比如下面的這段代碼就有map,

return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')

map也是一個數(shù)據(jù)結(jié)構(gòu),它背后的知識非常復(fù)雜,但是我們只需了解它是做什么的就可以,如果你想對數(shù)組里面的每一個值進行函數(shù)操作并且返回一個新數(shù)組,那你可以使用map。

上面這段代碼就是對數(shù)組[year, month, day]和[hour, minute, second]里面的每一個數(shù)值都進行格式化數(shù)字的操作,這一點我們可以在file.js的onLoad里打印看效果就明白了

  onLoad: function (options) {
    console.log('2019年9月2日map處理后的結(jié)果', [2019,9,2].map(util.formatNumber))
    console.log('上午9點13分4秒map處理后的結(jié)果', [9, 13, 4].map(util.formatNumber))
  },

從控制臺打印的結(jié)果就可以看到數(shù)組里面的數(shù)字被格式化處理,有兩位數(shù)的不處理,沒有2位數(shù)的前面加0,而且返回的也是數(shù)組。至于數(shù)組Array的join方法,就是將數(shù)組元素拼接為字符串,以分隔符分割,上面[year, month, day]分隔符為”/”,[hour, minute, second]的分隔符為”:”。

數(shù)據(jù)緩存Storage

logs頁面還涉及到數(shù)據(jù)緩存Storage方面的知識。通過前面的學(xué)習(xí),我們了解到點擊事件生成的事件對象也好,使用數(shù)據(jù)表單提交的數(shù)據(jù)也好,還是上傳的圖片、文件也好,只要我們重新編譯小程序,這些數(shù)據(jù)都會消失。前面我們也提到存儲數(shù)據(jù)、文件的方式有三種,一是保存到本地手機、二就是緩存,三是上傳到服務(wù)器(云開發(fā)會講解),這里我們就來了解數(shù)據(jù)緩存方面的知識。

技術(shù)文檔:wx.setStorageSync()、wx.getStorageSync()

保存文件

注意:盡管上傳圖片和上傳文件都是把圖片或文件先上傳到臨時文件里,但是保存圖片wx.saveImageToPhotosAlbum()和保存文件wx.saveFile()是完全不同的概念,保存圖片是把圖片保存到手機本地相冊;而保存文件則是把圖片由臨時文件移動到本地存儲里,而本地存儲每個小程序用戶只有10M的空間。

保存文件技術(shù)文檔:wx.saveFile()

在了解logs的數(shù)據(jù)緩存案例之前,我們先來看一個將上傳的圖片由臨時文件保存到緩存的案例,使用開發(fā)者工具在file.wxml里輸入以下代碼:

<view>臨時文件的圖片</view>
<image mode="widthFix" src="{{tempFilePath}}" style="width:100px"></image>
<view>緩存保存的圖片</view>
<image mode="widthFix" src="{{savedFilePath}}" style="width:100px"></image>
<button  bindtap="chooseImage">請選擇文件</button>
<button  bindtap="saveImage">保存文件到緩存</button>

然后在file.js的data里初始化臨時文件的路徑tempFilePath和本地緩存的路徑savedFilePath:

  data: {
    tempFilePath: '',
    savedFilePath: '',
  },

再在file.js里添加事件處理函數(shù)chooseImage和saveImage(函數(shù)名有別于之前的chooseImg和saveImg,不要弄混了哦):

  chooseImage:function() {
    const that = this
    wx.chooseImage({
      count: 1,
      success(res) {
        that.setData({
          tempFilePath: res.tempFilePaths[0]
        })
      }
    })
  },
  saveImage:function() {
    const that = this
    wx.saveFile({
      tempFilePath: this.data.tempFilePath,
      success(res) {
        that.setData({
          savedFilePath: res.savedFilePath
        })
        wx.setStorageSync('savedFilePath', res.savedFilePath)
      },
    })
  },

還沒有完~我們還需要在file.js的onLoad生命周期函數(shù)里將緩存里存儲的路徑賦值給本地緩存的路徑savedFilePath:

  onLoad: function (options) {
    this.setData({
      savedFilePath: wx.getStorageSync('savedFilePath')
    })
  },

編譯之后,點擊請上傳文件的button,會觸發(fā)chooseImage事件處理函數(shù),然后調(diào)用上傳圖片的API wx.chooseImage,這時會將圖片上傳到臨時文件,并將取得的臨時文件地址賦值給tempFilePath,有了tempFilePath,圖片就能渲染出來了。

然后再點擊保存文件到緩存的button,會觸發(fā)saveImage事件處理函數(shù),然后保存文件API wx.saveFile,將tempFilePath里的圖片保存到緩存,并將取得的緩存地址賦值給savedFilePath(注意tempFilePath也就是臨時路徑是保存文件的必備參數(shù)),這時緩存保存的圖片就渲染到頁面了。然后會再來調(diào)用緩存API wx.setStorageSync(),將緩存文件的路徑保存到緩存的key savedFilePath里面。有些參數(shù)名稱相同但是含義不同,這個要注意。

通過wx.setStorageSync()保存到緩存里的數(shù)據(jù),可以使用wx.getStorageSync()獲取出來,我們在onLoad里把獲取出來的緩存文件路徑再賦值給savedFilePath。

編譯頁面,看看臨時文件與緩存文件的不同,臨時文件由于小程序的編譯會被清除掉,而緩存文件有10M的空間,只要用戶不刻意刪除,它就會一直在。

緩存的好處非常多,比如用戶的瀏覽文章、播放視頻的進度(看了哪些文章,給個特別的樣式,免得用戶不知道看到哪里了)、用戶的登錄信息(用戶登錄一次,可以很長時間不用再登錄)、自定義的模板樣式(用戶選擇自己喜歡的樣式,下次打開小程序還是一樣)、最經(jīng)常使用的小圖片(保存在緩存,下次打開小程序速度更快)等等。

logs的數(shù)據(jù)緩存處理

我們再回頭看logs的緩存案例,在小程序app.js的生命周期函數(shù)onLaunch里輸入以下代碼,也就是在小程序初始化的時候就執(zhí)行日志進行記錄:

    //  ||為邏輯與,就是聲明logs為獲取緩存里的logs記錄,沒有時就為空數(shù)組
    var logs = wx.getStorageSync('logs') || []

 
    //unshift()是數(shù)組的操作方法,它會將一個或多個元素添加到數(shù)組的開頭,這樣最新的記錄就放在數(shù)組的最前面,
    //這里是把Date.now()獲取到的時間戳放置到數(shù)組的最前面
    logs.unshift(Date.now())

 
   //將logs數(shù)據(jù)存儲到緩存指定的key也就是logs里面
    wx.setStorageSync('logs', logs)
    console.log(logs)
    console.log(Date.now())

當(dāng)我們不斷編譯,logs數(shù)組里面的記錄會不斷增加,增加的值都是時間戳。那如何把緩存里面的日志給渲染到頁面呢?

在file.wxml里添加以下代碼,由于logs是數(shù)組,我們使用列表渲染,這里有個數(shù)組的index值,由于index是從0開始記錄,給index加1,符合我們?nèi)粘J褂昧?xí)慣。

<view wx:for="{{logs}}" wx:for-item="log">
    <view>{{index + 1}}. {{log}}</view>
</view>

然后在file.js的data里初始化logs

  data: {
    logs: []
  },

然后再在file.js的生命周期函數(shù)onLoad里把緩存里的日志取出來通過setData賦值給data里的logs

  onLoad: function () {
    this.setData({
      logs: (wx.getStorageSync('logs') || []).map(log => {
        return util.formatTime(new Date(log))
      })
    })
  },

結(jié)合前面所了解的map、模塊化知識就不難理解上面的這段代碼了。緩存有同步API和異步API的區(qū)別,結(jié)合之前我們了解的同步和異步的知識,看看緩存的同步API與異步API的區(qū)別。

注意:打開開發(fā)者工具調(diào)試面板的Storage標簽頁,小程序的緩存記錄都會在這里可以直觀的看到,調(diào)試時可以留意,這一點非常重要。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號