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