国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

云開發(fā) 存儲(chǔ)、數(shù)組、對(duì)象

2020-07-20 11:22 更新

在云開發(fā)能力章節(jié)我們了解到小程序端和服務(wù)端都可以上傳文件到云存儲(chǔ),不過在實(shí)際開發(fā)中云存儲(chǔ)里的文件鏈接需要被記錄在數(shù)據(jù)庫里才方便調(diào)用。接下來我們就來介紹云存儲(chǔ)文件的增刪改查是如何與數(shù)據(jù)庫的增刪改查結(jié)合在一起的。在云數(shù)據(jù)庫入門章節(jié)我們所涉及到的數(shù)據(jù)庫里數(shù)據(jù)類型還非常簡單,在這一章里我們會(huì)來介紹如何操作數(shù)據(jù)庫的數(shù)組和對(duì)象等復(fù)雜數(shù)據(jù)類型的增刪改查。

云存儲(chǔ)與數(shù)據(jù)庫的關(guān)系

不經(jīng)過數(shù)據(jù)庫直接把文件上傳到云存儲(chǔ)里,這樣文件的上傳、刪除、修改、查詢是無法和具體的業(yè)務(wù)對(duì)應(yīng)的,比如文章商品的配圖、表單圖片附件的添加與刪除,都需要圖片等資源能夠與文章、商品、表單的ID能夠一一對(duì)應(yīng)才能進(jìn)行管理(在數(shù)據(jù)庫里才能對(duì)應(yīng)),而這些文章、商品、表單又可以通過數(shù)據(jù)庫與用戶的ID、其他業(yè)務(wù)聯(lián)系起來,可見數(shù)據(jù)庫在云存儲(chǔ)的管理上扮演著極其重要的角色。

數(shù)據(jù)庫的設(shè)計(jì)與結(jié)構(gòu)

和Excel表、關(guān)系型數(shù)據(jù)庫(如MySQL)以行和列、多表關(guān)系來設(shè)計(jì)表結(jié)構(gòu)不同的是,云開發(fā)的數(shù)據(jù)庫是基于文檔的。我們可以在一個(gè)記錄里嵌套多層數(shù)組和對(duì)象,把每個(gè)文檔所需要的數(shù)據(jù)都嵌入到一個(gè)文檔里,而不是分散到多個(gè)不同的集合。

比如我們想做一個(gè)網(wǎng)盤小程序,用來記錄用戶信息,以及創(chuàng)建的相冊(cè)、文件夾,這里相冊(cè)和文件夾因?yàn)榭梢詣?chuàng)建很多個(gè),所以它是一個(gè)數(shù)組;而每一個(gè)相冊(cè)對(duì)象和文件夾對(duì)象里都可以存儲(chǔ)一個(gè)照片列表和文件列表,我們發(fā)現(xiàn)在云開發(fā)數(shù)據(jù)庫里一個(gè)元素的值是數(shù)組,數(shù)組里又嵌套對(duì)象,對(duì)象里又有元素是數(shù)組是非常常見的事情。

以下是網(wǎng)盤小程序的數(shù)據(jù)庫設(shè)計(jì),包含了一個(gè)用戶的信息,上傳的所有文件和照片等信息:

{
  "_id": "自動(dòng)生成的ID",
  "_openid": "用戶在當(dāng)前小程序的openid",
  "nickName": "用戶的昵稱",
  "avatarUrl": "用戶的頭像鏈接",
  "albums": [
    {
      "albumName": "相冊(cè)名稱",
      "coverURL": "相冊(cè)封面地址",
      "photos": [
        {
          "comments": "照片備注",
          "fileID": "照片的地址"
        }
      ]
    }
  ],
  "folders": [
    {
      "folderName": "文件夾名稱",
      "files": [
        {
          "name": "文件名稱",
          "fileID": "文件的地址",
          "comments": "文件備注"
        }
      ]
    }
  ]
}

如果是用關(guān)系型數(shù)據(jù)庫,就會(huì)建user表來存儲(chǔ)用戶信息,albums表存儲(chǔ)相冊(cè)信息,folders表存儲(chǔ)文件夾信息,photos表存儲(chǔ)照片信息,files表存儲(chǔ)文件信息,相信大家可以通過這個(gè)案例對(duì)云數(shù)據(jù)庫是面向文檔的有一個(gè)大致的了解。

當(dāng)然云開發(fā)的數(shù)據(jù)庫也是可以把數(shù)據(jù)分散到不同集合的,需要視不同的情況而定,在后面章節(jié)我們會(huì)介紹。這種將每個(gè)文檔所需的數(shù)據(jù)都嵌入到一個(gè)文檔內(nèi)部的做法,我們稱之為反范式化(denormalization),將數(shù)據(jù)分散到多個(gè)不同的集合,不同集合之間相互引用稱之為范式化(normalization),也就是說反范式化文檔里包含子文檔,而范式化呢,文檔的子文檔則是存儲(chǔ)在另一個(gè)集合之中。

fileID是存儲(chǔ)與數(shù)據(jù)庫的紐帶

從上面可以看出,云存儲(chǔ)與數(shù)據(jù)庫就是通過fileID來取得聯(lián)系的,數(shù)據(jù)庫只記錄文件在云存儲(chǔ)的fileID,我們可以訪問數(shù)據(jù)庫相應(yīng)的fileID屬性進(jìn)行記錄的增刪改查操作,與此同時(shí)調(diào)用云存儲(chǔ)的上傳文件、下載文件、刪除文件等API,這樣云存儲(chǔ)就被數(shù)據(jù)庫給管理起來了。

打開云開發(fā)技術(shù)文檔里云存儲(chǔ)的所有API,如上傳文件uploadFile、下載文件downloadFile、刪除文件deleteFile、用云文件 ID 換取真實(shí)鏈接getTempFileURL,我們發(fā)現(xiàn)這些API始終是圍繞fileID來展開的,要么fileID是success回調(diào)返回的對(duì)象,要么fileID是API必備的屬性。

建立用戶與數(shù)據(jù)的關(guān)系

openid與云開發(fā)

在前面我們已經(jīng)了解到,用戶在小程序里有著獨(dú)一無二的openid,用openid完全可以區(qū)分用戶;使用云開發(fā)時(shí)用戶在小程序端上傳文件到云存儲(chǔ),這個(gè)openid會(huì)被記錄在文件信息里;添加數(shù)據(jù)到數(shù)據(jù)庫這個(gè)openid會(huì)被保存在_openid的字段里(也就是說我們除了可以用云函數(shù)如前面的login來獲取用戶的openid,還可以通過數(shù)據(jù)庫的_openid字段來獲取openid);而且我們?cè)谛〕绦蚨瞬樵償?shù)據(jù)時(shí)(查詢時(shí)改、刪、更新等的前提),都會(huì)默認(rèn)有一個(gè) where({_openid:當(dāng)前用戶的openid})的條件,限制了用戶write寫(改、刪、更新)的權(quán)限。

_id與云開發(fā)

當(dāng)用戶在小程序端往數(shù)據(jù)庫用Collection.add添加記錄document時(shí),會(huì)自動(dòng)給該記錄生成_id,同時(shí)也會(huì)創(chuàng)建一個(gè)_openid,_id和_openid由于都是獨(dú)一無二的,只要我們獲取每個(gè)用戶創(chuàng)建的記錄_id,也就能同時(shí)確定這個(gè)用戶的openid。

判斷用戶是否存在并創(chuàng)建記錄

打開云開發(fā)控制臺(tái)的數(shù)據(jù)庫標(biāo)簽,新建一個(gè)clouddisk的集合,并修改它的權(quán)限為為“所有人可讀,僅創(chuàng)建者可讀寫”(或使用安全規(guī)則)。使用開發(fā)者工具新建一個(gè)folder的頁面,然后在folder.js的頁面生命周期函數(shù)onLoad里輸入以下代碼:

this.checkUser()

this調(diào)用自定義函數(shù),開發(fā)者可以添加任意的函數(shù)或數(shù)據(jù)到 Object 參數(shù)中,在頁面的函數(shù)中用 this 可以訪問

然后再在Page()對(duì)象里輸入以下代碼,代碼的意思是如果clouddisk里沒有用戶創(chuàng)建的數(shù)據(jù),那就在clouddisk里新增一條記錄;如果有數(shù)據(jù),就返回?cái)?shù)據(jù):

  async checkUser() {
    //獲取clouddisk是否有當(dāng)前用戶的數(shù)據(jù),注意這里默認(rèn)帶了一個(gè)where({_openid:"當(dāng)前用戶的openid"})的條件
    const userData = await db.collection('clouddisk').get() 
    console.log("當(dāng)前用戶的數(shù)據(jù)對(duì)象",userData)

 
    //如果當(dāng)前用戶的數(shù)據(jù)data數(shù)組的長度為0,說明數(shù)據(jù)庫里沒有當(dāng)前用戶的數(shù)據(jù)
    if(userData.data.length === 0){      
      //沒有當(dāng)前用戶的數(shù)據(jù),那就新建一個(gè)數(shù)據(jù)框架,其中_id和_openid會(huì)自動(dòng)生成
      return await db.collection('clouddisk').add({
        data:{
          //nickName和avatarUrl可以通過getUserInfo來獲取,這里不多介紹
          "nickName": "", 
          "avatarUrl": "",
          "albums": [ ],
          "folders": [ ]
        }
      })
    }else{
      this.setData({
        userData
      })
      console.log('用戶數(shù)據(jù)',userData)
    }
  },

一個(gè)用戶只能創(chuàng)建一條記錄,如果是開一個(gè)用戶可以創(chuàng)建多條記錄…

預(yù)先搭好文檔的數(shù)據(jù)框架方便我們?cè)诤竺嬉評(píng)pdate的方式來更新數(shù)據(jù)。

async/await的使用說明

async 是“異步”的簡寫,async 用于申明一個(gè) function 是異步的,而 await 用于等待一個(gè)異步方法執(zhí)行完成,await 只能出現(xiàn)在 async 函數(shù)中。await 在 async 函數(shù)中才會(huì)有效。假設(shè)一個(gè)業(yè)務(wù)需要分步完成,每個(gè)步驟都是異步的,而且依賴上一步的執(zhí)行結(jié)果,甚至依賴之前每一步的結(jié)果,就可以使用Async Await來完成

小程序端現(xiàn)在完全支持async/await的寫法,不過需要在開發(fā)者工具-詳情-本地設(shè)置,勾選增強(qiáng)編譯才行,否則會(huì)報(bào)以下錯(cuò)誤。

Uncaught ReferenceError: regeneratorRuntime is not defined

async 函數(shù)返回值是 Promise 對(duì)象, async 函數(shù)內(nèi)部 return 返回的值。會(huì)成為 then 方法回調(diào)函數(shù)的參數(shù)。如果 async 函數(shù)內(nèi)部拋出異常,則會(huì)導(dǎo)致返回的 Promise 對(duì)象狀態(tài)變?yōu)? reject 狀態(tài)。拋出的錯(cuò)誤而會(huì)被 catch 方法回調(diào)函數(shù)接收到。async 函數(shù)返回的 Promise 對(duì)象,必須等到內(nèi)部所有的 await 命令的 Promise 對(duì)象執(zhí)行完,才會(huì)發(fā)生狀態(tài)改變。也就是說,只有當(dāng) async 函數(shù)內(nèi)部的異步操作都執(zhí)行完,才會(huì)執(zhí)行 then 方法的回調(diào)。

在async函數(shù)中使用await,那么await這里的代碼就會(huì)變成同步的了,意思就是說只有等await后面的Promise執(zhí)行完成得到結(jié)果才會(huì)繼續(xù)下去,await就是等待,這樣雖然避免了異步,但是它也會(huì)阻塞代碼,所以使用的時(shí)候要考慮周全。await會(huì)阻塞代碼,每個(gè)await都必須等后面的fn()執(zhí)行完成才會(huì)執(zhí)行下一行代碼

云存儲(chǔ)文件夾管理

在小程序端創(chuàng)建一個(gè)文件夾,需要考慮三個(gè)方面,一是文件夾在云存儲(chǔ)里是怎么創(chuàng)建的;二是文件夾在數(shù)據(jù)庫里的表現(xiàn)形式;三是小程序端頁面應(yīng)該怎么交互才算是創(chuàng)建了一個(gè)文件夾;

文件夾在云存儲(chǔ)里是怎么創(chuàng)建的

在云開發(fā)能力章節(jié)我們了解到,要上傳demo.jpg到云存儲(chǔ)的cloudbase文件夾里,只需要指明cloudPath云存儲(chǔ)的路徑為cloudbase/demo.jpg即可,這里的cloudbase文件夾,在我們上傳文件時(shí)代碼會(huì)自動(dòng)創(chuàng)建,也就是說我們?cè)谛〕绦蚨藙?chuàng)建文件夾不需要對(duì)云存儲(chǔ)做任何事情,因?yàn)樵谠拼鎯?chǔ)這里,文件夾是只有在文件上傳時(shí)才會(huì)創(chuàng)建。

文件夾在數(shù)據(jù)庫里的表現(xiàn)形式

盡管文件夾在小程序端的頁面交互看來非常復(fù)雜,但是它在數(shù)據(jù)庫的形式看起來卻非常簡單,我們創(chuàng)建文件夾只是在操作(增刪改查)數(shù)組和對(duì)象而已,以下的folders數(shù)組是文件夾列表,而一個(gè)文件夾只是數(shù)組里的一個(gè)對(duì)象而已。

"folders": [
    {
      "folderName": "文件夾名稱",
      "files": [ ]
    }
  ]

文件夾的創(chuàng)建與頁面交互

通過前面的分析可知,在小程序端創(chuàng)建文件夾,只會(huì)操作數(shù)據(jù)庫的數(shù)據(jù),而不會(huì)操作云存儲(chǔ),我們來看具體的代碼實(shí)現(xiàn)。使用開發(fā)者工具新建一個(gè)folder的頁面,然后在folder.wxml里輸入以下代碼:

<form bindsubmit="formSubmit">
   <input name="name" placeholder='請(qǐng)輸入文件夾名' auto-focus value='{{inputValue}}' bindinput='keyInput'></input>
    <button type="primary" formType="submit">新建文件夾</button>
</form>

方法一:使用push和

在folder.js里輸入以下代碼:

  async createFolder(e) {
    let foldersName = e.detail.value.foldersName
    const folders = this.data.userData.data[0].folders
    folders.push({ foldersName: foldersName, files: [] })
    const _id= this.data.userData.data[0]._id
    return await db.collection('clouddisk').doc(_id).update({
      data: {
        folders: _.set(folders)
      }
    })
  },

技術(shù)文檔:字段更新操作符set

方法二:

在folder.js里輸入以下代碼:

  async createFolder(e) {
    let foldersName = e.detail.value.foldersName
    const _id= this.data.userData.data[0]._id
    return await db.collection('clouddisk').doc(_id).update({
      data: {
        folders: _.push([{ foldersName: foldersName, files: [] }])
      }
    })
  },

技術(shù)文檔:數(shù)組更新操作符push

先讀后寫與先寫后讀

上傳單個(gè)文件到文件夾

相信大家都應(yīng)該在其他小程序體驗(yàn)過文件上傳的功能,在交互上這個(gè)功能雖然看起來簡單,但是在代碼的邏輯上卻包含著四個(gè)關(guān)鍵步驟:

  • 首先把文件上傳到小程序的臨時(shí)文件,并獲取臨時(shí)文件地址以及文件的名稱;
  • 將臨時(shí)文件上傳到云存儲(chǔ)指定云文件里,并q取到文件的FileID;
  • 將文件在云存儲(chǔ)的FileID和文件的名稱上傳到數(shù)據(jù)庫;
  • 獲取文件夾內(nèi)所有文件的信息。

上傳文件到小程序的臨時(shí)文件

使用開發(fā)者工具在folder.wxml里輸入以下代碼:

<form bindsubmit="uploadFiles"> 
   <button type="primary" bindtap="chooseMessageFile">選擇文件</button>
   <button type="primary" formType="submit">上傳文件</button>
</form>

然后在folder.js里輸入以下代碼:

  chooseMessageFile(){
    const files = this.data.files
    wx.chooseMessageFile({
      count: 5,
      success: res => {
        console.log('選擇文件之后的res',res)
        let tempFilePaths = res.tempFiles
        for (const tempFilePath of tempFilePaths) {
          files.push({
            src: tempFilePath.path,
            name: tempFilePath.name
          })
        }
        this.setData({ files: files })
        console.log('選擇文件之后的files', this.data.files)
      }
    })
  },

將臨時(shí)文件上傳到云存儲(chǔ)

技術(shù)文檔:wx.cloud.uploadFile

  uploadFiles(e) {
    const filePath = this.data.files[0].src
    const cloudPath = `cloudbase/${Date.now()}-${Math.floor(Math.random(0, 1) * 1000)}` + filePath.match(/\.[^.]+?$/)
    wx.cloud.uploadFile({   
      cloudPath,filePath
    }).then(res => {
      this.setData({
        fileID:res.fileID
      })      
    }).catch(error => {
      console.log("文件上傳失敗",error)
    })
  },

上傳成功后會(huì)獲得文件唯一標(biāo)識(shí)符,即文件 ID,后續(xù)操作都基于文件 ID 而不是 URL。

將文件信息存儲(chǔ)到數(shù)據(jù)庫

  addFiles(fileID) {
    const name = this.data.files[0].name
    const _id= this.data.userData.data[0]._id
    db.collection('clouddisk').doc(_id).update({
      data: {
        'folders.0.files': _.push({
          "name":name,
          "fileID":fileID
        })
      }
    }).then(result => {
      console.log("寫入成功", result)
      wx.navigateBack()
    }
    )
  }

匹配數(shù)組第 n 項(xiàng)元素 如果想找出數(shù)組字段中數(shù)組的第 n 個(gè)元素等于某個(gè)值的記錄,那在 <key, value> 匹配中可以以 字段.下標(biāo) 為 key,目標(biāo)值為 value 來做匹配。如對(duì)上面的例子,如果想找出 number 字段第二項(xiàng)的值為 20 的記錄,可以如下查詢(注意:數(shù)組下標(biāo)從 0 開始)

獲取文件夾內(nèi)文件列表

在onload生命周期函數(shù)里輸入

this.getFiles()

然后再在Page對(duì)象里添加getFiles()方法,獲取該用戶的數(shù)據(jù)

  getFiles(){
    const _id= this.data.userData.data[0]._id
    db.collection("clouddisk").doc(_id).get()
    .then(res => {
      console.log('用戶數(shù)據(jù)',res.data)
    })
    .catch(err => {
      console.error(err)
    })
  }

要實(shí)際開發(fā)一個(gè)具體的功能,一定要先思考這個(gè)功能的頁面交互是怎樣的,而頁面交互的背后都只不過是簡單的數(shù)據(jù),但正是這些簡單的數(shù)據(jù)經(jīng)過頁面交互處理之后卻“蒙蔽”了用戶的雙眼,讓用戶覺得復(fù)雜,覺得這個(gè)功能真實(shí)存在。

嵌套數(shù)組和對(duì)象的查詢

我們可以對(duì)對(duì)象、對(duì)象中的元素、數(shù)組、數(shù)組中的元素進(jìn)行匹配查詢,甚至還可以對(duì)數(shù)組和對(duì)象相互嵌套的字段進(jìn)行匹配查詢/更新

匹配記錄中的嵌套字段

// 方式一
db.collection('todos').where({
  style: {
    color: 'red'
  }
}).get()

 
// 方式二
db.collection('todos').where({
  'style.color': 'red'
}).get()

匹配并更新數(shù)組中的元素

上傳多個(gè)文件到文件夾

查詢所有數(shù)據(jù)

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()
const MAX_LIMIT = 100
exports.main = async (event, context) => {
  // 先取出集合記錄總數(shù)
  const countResult = await db.collection('china').count()
  const total = countResult.total
  // 計(jì)算需分幾次取
  const batchTimes = Math.ceil(total / 100)
  // 承載所有讀操作的 promise 的數(shù)組
  const tasks = []
  for (let i = 0; i < batchTimes; i++) {
    const promise = db.collection('china').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
    tasks.push(promise)
  }
  // 等待所有
  return (await Promise.all(tasks)).reduce((acc, cur) => {
    return {
      data: acc.data.concat(cur.data),
      errMsg: acc.errMsg,
    }
  })
}

小程序端下載并預(yù)覽文件

技術(shù)文檔:wx.openDocument()、wx.cloud.downloadFile

使用云開發(fā)來下載云存儲(chǔ)里面的文件,就不會(huì)有域名校驗(yàn)備案的問題

  previewFile(){
    wx.cloud.downloadFile({
      fileID: 'cloud://xly-xrlur.786c-xly-xrlur-1300446086/cloudbase/技術(shù)工坊預(yù)備手冊(cè).pdf'
    }).then(res => {
        const filePath = res.tempFilePath
        wx.openDocument({
          filePath: filePath
        })
    }).catch(error => {
      console.log(error)
    })
  }

刪除記錄與刪除字段

技術(shù)文檔:deleteFile

可以根據(jù)文件 ID 下載文件,用戶僅可下載其有訪問權(quán)限的文件:

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

 
exports.main = async (event, context) => {
  const fileIDs = ['xxx', 'xxx']
  const result = await cloud.deleteFile({
    fileList: fileIDs,
  })
  return result.fileList
}

嵌套刪除字段

  return await db.collection("clouddisk").doc("_id").update({
   data:{
    "folders.0.files.1": _.remove()
   }
  })

獲取臨時(shí)鏈接并分享文件

技術(shù)文檔:getTempFileURL

將服務(wù)端的文件傳到小程序端

技術(shù)文檔:downloadFile

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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)