通過Nodejs的模塊,我們可以實(shí)現(xiàn)云函數(shù)與服務(wù)端的文件系統(tǒng)進(jìn)行一定的交互,比如在前面我們就使用云函數(shù)將服務(wù)端的圖片先使用fs.createReadStream讀取,然后上傳到云存儲(chǔ)。Nodejs的文件處理能力讓云函數(shù)也能操作服務(wù)端的文件,比如文件查找、讀取、寫入乃至代碼編譯。
還是以nodefile云函數(shù)為例,使用微信開發(fā)者工具在nodefile云函數(shù)下新建一個(gè)文件夾,比如assets,然后在assets里放入demo.jpg圖片文件以及index.html網(wǎng)頁文件等,目錄結(jié)構(gòu)如下所示:
nodefile // 云函數(shù)目錄
├── config //config文件夾
│ └── config.js //config.js文件
├── assets //assets文件夾
│ └── demo.jpg
│ └── index.html
└── index.js
└── config.json
└── package.json
然后再在nodefile云函數(shù)的index.js里輸入以下代碼,使用fs.createReadStream讀取云函數(shù)目錄下的文件:
const cloud = require('wx-server-sdk')
const fs = require('fs')
const path = require('path')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
const fileStream = fs.createReadStream(path.join(__dirname, './assets/demo.jpg'))
return await cloud.uploadFile({
cloudPath: 'demo.jpg',
fileContent: fileStream,
})
}
上面的案例使用到了Nodejs文件處理必不可少的fs模塊,fs 模塊: 可以實(shí)現(xiàn)文件目錄的創(chuàng)建、刪除、查詢以及文件的讀取和寫入:
上面只大致列舉了fs模塊的一些方法,關(guān)于如何使用大家可以去參考Nodejs官方技術(shù)文檔,當(dāng)然還有fs.Stats類,封裝了文件信息相關(guān)的操作;fs.Dir類,封裝了和文件目錄相關(guān)的操作;fs.Dirent類,封裝了目錄項(xiàng)的相關(guān)操作等等。
Nodejs fs模塊中的方法都有異步和同步版本,比如讀取文件內(nèi)容的方法有異步的 fs.readFile() 和同步的 fs.readFileSync()。異步的方法函數(shù)最后一個(gè)參數(shù)為回調(diào)函數(shù)callback,回調(diào)函數(shù)的參數(shù)里都包含了錯(cuò)誤信息(error),通常建議大家使用異步方法,性能更高,速度更快,而且沒有阻塞。
操作文件之時(shí),不可避免的都會(huì)使用到path模塊,path 模塊: 提供了一些用于處理文件路徑的API,它的常用方法有:
Node讀取文件有兩種方式,一是利用fs.readFile來讀取,還有一個(gè)是使用流fs.createReadStream來讀取。如果要讀取的文件比較小,我們可以使用fs.readFile,fs.readFile讀取文件是將文件一次性讀取到本地內(nèi)存。而如果讀取一個(gè)大文件,比如當(dāng)文件超過16M左右的時(shí)候(文件越大性能也就會(huì)越大),一次性讀取就會(huì)占用大量的內(nèi)存,效率比較低,這個(gè)時(shí)候需要用流來讀取。流是將文件數(shù)據(jù)分割成一段段的讀取,可以控制速率,效率比較高,不會(huì)占用太大的內(nèi)存。無論文件是大是小,我們都可以使用fs.createReadStream來讀取文件。
為了讓大家看的更加明白一些,我們?cè)倏聪旅孢@個(gè)案例,使用云函數(shù)來讀取云函數(shù)在云端的目錄下有哪些文件(也就是列出云函數(shù)目錄下的文件清單):
const cloud = require('wx-server-sdk')
const fs = require('fs')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
const funFolder = '.';//.表示當(dāng)前目錄
fs.readdir(funFolder, (err, files) => {
files.forEach(file => {
console.log(file);
});
});
}
上面就用到了fs.readdir()方法,以異步的方式讀取云函數(shù)在服務(wù)端的目錄下面所有的文件。
我們需要注意的是,云函數(shù)的目錄文件只有讀權(quán)限,沒有寫權(quán)限,我們不能把文件寫入到云函數(shù)目錄文件里,也不能修改或刪除里面的文件。但是每個(gè)云函數(shù)實(shí)例都在 /tmp 目錄下提供了一塊 512MB 的臨時(shí)磁盤空間用于處理單次云函數(shù)執(zhí)行過程中的臨時(shí)文件讀寫需求,我們可以用云函數(shù)來對(duì) /tmp 進(jìn)行文件的增刪改查等的操作,這些模塊知識(shí)依然派的上用場(chǎng)。
我們還可以結(jié)合結(jié)合Nodejs文件操作的知識(shí),使用云函數(shù)在 /tmp 臨時(shí)磁盤空間創(chuàng)建一個(gè)txt文件,然后將創(chuàng)建的文件上傳到云存儲(chǔ)。
const cloud = require('wx-server-sdk')
const fs = require('fs')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
exports.main = async (event, context) => {
//創(chuàng)建一個(gè)文件
const text = "云開發(fā)技術(shù)訓(xùn)練營(yíng)CloudBase Camp. ";
await fs.writeFile("/tmp/tcb.txt", text, 'utf8', (err) => { //將文件寫入到臨時(shí)磁盤空間
if (err) console.log(err);
console.log("成功寫入文件.");
});
//將創(chuàng)建的txt文件上傳到云存儲(chǔ)
const fileStream = await fs.createReadStream('/tmp/tcb.txt')
return await cloud.uploadFile({
cloudPath: 'tcb.txt',
fileContent: fileStream,
})
}
上面創(chuàng)建文件使用的是fs.writeFile()方法,我們也可以使用fs.createWriteStream()的方法來處理:
const writeStream = fs.createWriteStream("tcb.txt");
writeStream.write("云開發(fā)技術(shù)訓(xùn)練營(yíng). ");
writeStream.write("Tencent CloudBase.");
writeStream.end();
注意,我們創(chuàng)建文件的目錄也就是臨時(shí)磁盤空間是一個(gè)絕對(duì)路徑/tmp
,而不是云函數(shù)的當(dāng)前目錄.
,也就是說臨時(shí)磁盤空間獨(dú)立于云函數(shù),不在云函數(shù)目錄之下。
臨時(shí)磁盤空間有512M,可讀可寫,因此我們可以在云函數(shù)的執(zhí)行階段做一些文件處理的周轉(zhuǎn),但是這塊臨時(shí)磁盤空間在函數(shù)執(zhí)行完畢后可能被銷毀,不應(yīng)依賴和假設(shè)在磁盤空間存儲(chǔ)的臨時(shí)文件會(huì)一直存在。
Nodejs Buffer類的引入,讓云函數(shù)也擁有操作文件流或網(wǎng)絡(luò)二進(jìn)制流的能力,云函數(shù)通過downloadFile接口從云存儲(chǔ)里下載的數(shù)據(jù)類型就是Buffer,以及uploadFile接口可以將Buffer數(shù)據(jù)上傳到云存儲(chǔ)。Buffer 類在全局作用域中,因此我們無需使用 require('buffer')引入。
使用Buffer還可以進(jìn)行編碼轉(zhuǎn)換,比如下面的案例是將云存儲(chǔ)的圖片下載(這個(gè)數(shù)據(jù)類型是Buffer)通過buffer類的toString()方法轉(zhuǎn)換成base64編碼,并返回到小程序端。使用開發(fā)者工具新建一個(gè)downloading的云函數(shù),然后在index.js里輸入以下代碼:
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
exports.main = async (event, context) => {
const fileID = 'cloud://xly-xrlur.786c-xly-xrlur-1300446086/cloudbase/1576500614167-520.png'
//換成你云存儲(chǔ)內(nèi)的一張圖片的fileID,圖片不能過大
const res = await cloud.downloadFile({
fileID: fileID,
})
const buffer = res.fileContent
return buffer.toString('base64')
}
在小程序端創(chuàng)建一個(gè)事件處理函數(shù)getServerImg()來調(diào)用云函數(shù),將云函數(shù)返回的數(shù)據(jù)(base64編碼的圖片)賦值給data對(duì)象里的img,比如在一個(gè)頁面的js文件里輸入以下代碼:
data: {
img:""
},
getServerImg(){
wx.cloud.callFunction({
name: 'downloadimg',
success: res => {
console.log("云函數(shù)返回的數(shù)據(jù)",res.result)
this.setData({
img:res.result
})
},
fail: err => {
console.error('云函數(shù)調(diào)用失?。?, err)
}
})
}
在頁面的wxml文件里添加一個(gè)image組件(注意src的地址),當(dāng)點(diǎn)擊button時(shí),就會(huì)觸發(fā)事件處理函數(shù)來調(diào)用云函數(shù)將獲取到的base64圖片渲染到頁面。
<button bindtap="getServerImg">點(diǎn)擊渲染base64圖片</button>
<image width="400px" height="200px" src="data:image/jpeg;base64,{{img}}"></image>
云函數(shù)在處理圖片時(shí),將圖片轉(zhuǎn)成base64是有很多限制的,比如圖片不能過大,返回到小程序的數(shù)據(jù)大小不能超過1M,而且這些圖片最好是臨時(shí)性的文件,通常建議大家把處理好的圖片以云存儲(chǔ)為橋梁,將圖片處理好后上傳到云存儲(chǔ)獲取fileID,然后在小程序端直接渲染這個(gè)fileID即可。
Buffer還可以和字符串String、JSON等轉(zhuǎn)化,還可以處理ascii、utf8、utf16le、ucs2、binary、hex等編碼,可以進(jìn)行copy拷貝、concat拼接、indexOf查找、slice切片等等操作,這些都可以應(yīng)用到云函數(shù)里,就不一一介紹了,具體內(nèi)容可以閱讀Nodejs官方技術(shù)文檔。
通過云存儲(chǔ)來進(jìn)行大文件的傳輸從成本的角度上講也是有必要的,云函數(shù)將文件傳輸給云存儲(chǔ)使用的是內(nèi)網(wǎng)流量,速度快零費(fèi)用,小程序端獲取云存儲(chǔ)的文件走的是CDN,傳輸效果好,成本也比較低,大約0.18元/GB;云函數(shù)將文件發(fā)送給小程序端消耗的是云函數(shù)外網(wǎng)出流量,成本相對(duì)比較高,大約0.8元/GB。
更多建議: