可變文件系統(tǒng)(MFS)允許您像使用傳統(tǒng)的基于名稱的文件系統(tǒng)一樣,使用文件和目錄至会。
Lesson 1 IPFS簡(jiǎn)介
IPFS: 星際文件系統(tǒng)
IPFS现诀,或著稱為星際文件系統(tǒng),是用于在去中心化網(wǎng)絡(luò)上共享數(shù)據(jù)的對(duì)等(P2P)網(wǎng)絡(luò)協(xié)議知牌。 顧名思義,你可以將IPFS視為文件系統(tǒng)斤程,它具有一些獨(dú)特的特性角寸,使其成為安全的,去中心化共享的理想選擇忿墅。
如果你還沒(méi)有接觸過(guò)扁藕,我們建議你瀏覽我們的去中心化數(shù)據(jù)結(jié)構(gòu)教程,你可以在其中了解去中心化網(wǎng)絡(luò)的所有信息疚脐,以及它與你熟悉的傳統(tǒng)網(wǎng)絡(luò)之間的區(qū)別亿柑。 在那里,你將學(xué)習(xí)有關(guān)內(nèi)容尋址棍弄,加密哈希望薄,內(nèi)容標(biāo)識(shí)符(CID)以及在節(jié)點(diǎn)間共享信息的所有內(nèi)容,為了更好的理解本教程呼畸,所有這些概念都是你需要了解的痕支。
在IPFS中存儲(chǔ)和共享數(shù)據(jù)
當(dāng)內(nèi)容被添加到IPFS網(wǎng)絡(luò)時(shí),內(nèi)容將會(huì)被保存在哪里蛮原?
作為點(diǎn)對(duì)點(diǎn)數(shù)據(jù)存儲(chǔ)系統(tǒng)卧须,IPFS允許每個(gè)用戶(節(jié)點(diǎn))在本地托管任何他們喜歡的數(shù)據(jù)。 當(dāng)你第一次向IPFS添加內(nèi)容時(shí)儒陨,實(shí)際上只是將內(nèi)容通過(guò)IPFS協(xié)議轉(zhuǎn)換成共享格式花嘶,存儲(chǔ)在本地設(shè)備上。 通常蹦漠,我們是在自己的計(jì)算機(jī)上安裝IPFS椭员,并創(chuàng)建一個(gè)新的IPFS實(shí)例(也稱為節(jié)點(diǎn))。 這就是你的數(shù)據(jù)在本地保存的地方笛园,并通過(guò)內(nèi)容標(biāo)識(shí)符(CID)引用拆撼。 存儲(chǔ)在IPFS中的數(shù)據(jù)可以采用多種形式容劳,但最常見(jiàn)的使用場(chǎng)景之一,是對(duì)傳統(tǒng)文件的共享闸度,我們將在本教程中詳細(xì)了解竭贩。
當(dāng)你聯(lián)網(wǎng)時(shí),你可以選擇與其他節(jié)點(diǎn)共享你的數(shù)據(jù)或文件莺禁,但如果你是唯一一個(gè)托管該資源的人留量,則當(dāng)你的計(jì)算機(jī)離線時(shí),其他節(jié)點(diǎn)將無(wú)法使用該資源哟冬。 讓多個(gè)節(jié)點(diǎn)托管相同的文件可使資源更獲得更高的可用性楼熄,并且CID(通過(guò)cryptograhpic哈希創(chuàng)建的唯一內(nèi)容標(biāo)識(shí)符)的使用也保證了系統(tǒng)安全。 我們將在未來(lái)的教程中詳細(xì)討論共享浩峡,但是現(xiàn)在我們將專注于如何在你自己的IPFS實(shí)例中處理文件可岂。
可變文件系統(tǒng)(Mutable File System)
因?yàn)镮PFS中的文件是內(nèi)容尋址并且無(wú)法篡改的,所以你無(wú)法編輯文件; 因此翰灾,每次更改都會(huì)創(chuàng)建一個(gè)新文件缕粹。 可變文件系統(tǒng)(MFS)是一個(gè)內(nèi)置于IPFS中的工具,可以讓你像在基于名稱的文件系統(tǒng)中一樣處理文件 - 你可以添加纸淮,刪除平斩,移動(dòng)和編輯MFS文件,并自動(dòng)幫你完成更新鏈接和創(chuàng)建哈希的所有工作咽块。 它是一種抽象概念绘面,可以讓你像處理可變數(shù)據(jù)一樣,處理不可變數(shù)據(jù)侈沪。
可以通過(guò)調(diào)用IPFS CLI(命令行界面)和API中的files命令訪問(wèn)MFS揭璃。 在本教程中,我們將探索Files API亭罪。
如果你之前使用過(guò)命令行中的文件和目錄瘦馍,那么許多MFS方法看起來(lái)都因該非常熟悉!
讓我們開(kāi)始探索如何使用IPFS中的文件皆撩!
Lesson 2 查看目錄狀態(tài)
使用ProtoSchool中的文件
在我們的ProtoSchool教程中扣墩,當(dāng)你每次點(diǎn)擊課程中的“提交”按鈕時(shí)哲银,我們都會(huì)在瀏覽器中為您創(chuàng)建一個(gè)新的IPFS節(jié)點(diǎn)扛吞。 每當(dāng)你在我們的課程中看到ipfs.someMethod()時(shí),ipfs就是一個(gè)引用你的IPFS實(shí)例的變量荆责,也稱為節(jié)點(diǎn)滥比。 你執(zhí)行的操作僅影響你自己的IPFS節(jié)點(diǎn),而不影響屬于你的peers上的節(jié)點(diǎn)做院。
我們正在后臺(tái)創(chuàng)建你的IPFS節(jié)點(diǎn)盲泛,以便你可以專注于我們的課程濒持,但最終你需要學(xué)會(huì)安裝IPFS并在終端中運(yùn)行程序來(lái)啟動(dòng)你自己的本地節(jié)點(diǎn)。 當(dāng)你準(zhǔn)備好進(jìn)行實(shí)驗(yàn)時(shí)寺滚,可以在我們的文檔中找到有關(guān)安裝IPFS和初始化節(jié)點(diǎn)的說(shuō)明文檔柑营。
如前所述,與可變文件系統(tǒng)相關(guān)的方法是Files API的一部分村视,因此它們將采用ipfs.files.someMethod()的格式官套。 讓我們看一下即使在將任何文件添加到IPFS節(jié)點(diǎn)之前也可以開(kāi)始使用的簡(jiǎn)單方法。
使用ipfs.files.stat探索IPFS節(jié)點(diǎn)
使用IPFS節(jié)點(diǎn)時(shí)蚁孔,通常需要檢查文件或目錄的狀態(tài)奶赔。 您可以使用ipfs.files.stat執(zhí)行此操作,傳入你要查看的路徑杠氢。
例如站刑,要查看位于根目錄(/)中的名為stuff的目錄的狀態(tài),我們可以像這樣調(diào)用方法:
await ipfs.files.stat('/stuff')
此方法返回一個(gè)包含有關(guān)我們的文件或目錄的基本數(shù)據(jù)的對(duì)象:
- hash(帶有加密哈希的字符串)
- size(文件或目錄大小鼻百,以字節(jié)為單位的整數(shù))
- cumulativeSize(以字節(jié)為單位構(gòu)成DAGNodes文件的整數(shù)大薪事谩)
- type(可以是目錄或文件的字符串)
- blocks(如果type是目錄,這是目錄中的文件數(shù);如果type是文件愕宋,則是組成文件的塊數(shù))
- withLocality(一個(gè)布爾值玻靡,表示是否存在位置信息)
- local(一個(gè)布爾值,表示查詢的dag是否完全存在于本地)
- sizeLocal(一個(gè)整數(shù)中贝,表示本地存在的數(shù)據(jù)的累計(jì)大卸谀怼)
注意!目錄的size始終為0邻寿,無(wú)論它包含多少條目蝎土,因?yàn)槟夸泴?shí)際上只是一組指向其他文件和目錄的鏈接。相反绣否,目錄的cumulativeSize會(huì)隨著目錄內(nèi)容的變化而變化誊涯。它不僅代表該目錄中所有條目的文件大小,還代表描述這些條目的元數(shù)據(jù):類型蒜撮,塊大小等暴构。
需要注意的是,即使你的IPFS節(jié)點(diǎn)還沒(méi)有任何內(nèi)容段磨,也可以用stat對(duì)它進(jìn)行查看取逾。即使是空節(jié)點(diǎn)也有CID(哈希)。
Lesson 3 使用ProtoSchool中的文件
出于安全考慮苹支,Web瀏覽器不允許我們直接更改計(jì)算機(jī)文件系統(tǒng)中的文件砾隅。 因此,你需要將一個(gè)或多個(gè)文件上傳到本教程中使用的瀏覽器债蜜。
在每個(gè)練習(xí)中晴埂,你會(huì)發(fā)現(xiàn)可以通過(guò)拖放或從文件資源管理器中選擇文件來(lái)上傳文件究反。 如果仔細(xì)查看代碼編輯器中的run函數(shù),你會(huì)發(fā)現(xiàn)它現(xiàn)在需要一個(gè)參數(shù)files儒洛。 當(dāng)您從計(jì)算機(jī)上傳文件時(shí)精耐,我們必須確保它們作為文件數(shù)組傳遞給函數(shù)。 只要你不刷新瀏覽器琅锻,這些文件將在本教程的下一課中保持可訪問(wèn)狀態(tài)黍氮,但你也可以選擇上傳不同的文件以供每節(jié)課使用。
練習(xí)準(zhǔn)備工作浅浮,請(qǐng)從你的計(jì)算機(jī)上傳一個(gè)或多個(gè)文件沫浆,并查看瀏覽器收到的文件數(shù)組。
Lesson 4 將文件添加到MFS
現(xiàn)在我們可以在瀏覽器中訪問(wèn)文件滚秩,讓我們看看如何將它們添加到IPFS中专执。
要將文件添加到IPFS,我們可以使用MFS files.write方法郁油,如下所示:
await ipfs.files.write(path, content, [options])
MFS的 files.write方法可以接受Buffer本股,ReadableStream,PullStream桐腌,Blob(僅在瀏覽器中)或string path(僅在Node.js中)形式的文件拄显。瀏覽器中的文件對(duì)象是一種Blob格式,我們繼續(xù)案站!
即使瀏覽器中的文件對(duì)象碰巧知道自己的文件名躬审,Blob通常也不知道,因此IPFS無(wú)法直接確定真實(shí)的文件名蟆盐。我們必須提供所需的文件名作為path的一部分承边。
path是你要在IPFS實(shí)例中創(chuàng)建的新路徑,包括所需的文件名石挂。 (請(qǐng)注意博助,它是我們描述的目標(biāo)路徑,而不是文件已經(jīng)保存在你的計(jì)算機(jī)上的路徑痹愚。)
MFS files.write方法與你在自己的計(jì)算機(jī)上使用的方法類似富岳,實(shí)際上是為編輯現(xiàn)有文件的內(nèi)容而構(gòu)建的。不過(guò)拯腮,如果在給定的路徑不存在這個(gè)文件窖式,我們也可以通過(guò)一個(gè)布爾類型的選項(xiàng){create : true}來(lái)創(chuàng)建一個(gè)全新的文件。
因此疾瓮,如果我們的瀏覽器中有一個(gè)文件對(duì)象脖镀,可通過(guò)變量catPic訪問(wèn)飒箭,并且我們想將它添加到IPFS上的根目錄并將其命名為cat.jpg狼电,我們可以這樣做:
await ipfs.files.write('/cat.jpg', catPic, { create : true })
如果有必要蜒灰,我們可以使用拼接字符串(將字符串連接在一起)的方式來(lái)創(chuàng)建相同的路徑。 如果你的文件名是文件對(duì)象的屬性肩碟,這種方法在瀏覽器中使用就很方便强窖。
await ipfs.files.write('/' + catPic.name, catPic, { create : true })
請(qǐng)注意削祈,files.write方法不提供返回值髓抑。
稍后我們將介紹如何將文件添加到根目錄以外的目錄咙崎。
Lesson 5 查看目錄的內(nèi)容
當(dāng)我們使用files.write向MFS添加文件時(shí),該方法沒(méi)有返回任何值吨拍,但我們?nèi)匀豢梢詸z查以確保一切按預(yù)期工作。
在Mutable File System中伊滋,我們可以使用files.ls方法檢查目錄。 如果你曾使用命令行列出計(jì)算機(jī)上目錄的內(nèi)容队秩,那么對(duì)這種方法應(yīng)該會(huì)非常熟悉笑旺。
file.ls方法如下所示:
await ipfs.files.ls([path], [options])
該方法默認(rèn)列出根目錄(/)的內(nèi)容,或者你可以選擇指定要檢查的特定path(目錄)筒主,例如/ catPics鸟蟹,
file.ls生成一個(gè)對(duì)象數(shù)組,你正在查看的目錄中的每個(gè)文件或目錄戏锹,具有以下屬性:
- name(默認(rèn)值):文件名
- type({long : true} 可選項(xiàng)):對(duì)象的類型(0 -文件 或 1 - 目錄)
- size({long : true} 可選項(xiàng)):文件的大小(以字節(jié)為單位)
- hash({long : true} 可選項(xiàng)):表示IPFS文件的加密哈宪欤或內(nèi)容標(biāo)識(shí)符(CID)
注意奈搜!要返回所有這些屬性的值,必須使用選項(xiàng){long : true}猜极。 否則恨胚,除名稱字段外的所有屬性都將返回為0(對(duì)于size和type)或""(對(duì)于hash)
如果我們想查看 /catPics 目錄下所有內(nèi)容的詳細(xì)信息,我們可以這樣做:
await ipfs.files.ls('/catPics', { long : true })
你可以在files.ls API文檔中了解有關(guān)其他選項(xiàng)的更多信息来农。
Lesson 6 了解CID如何隨著數(shù)據(jù)的變化而變化
正如你在去中心化數(shù)據(jù)結(jié)構(gòu)教程中所了解到的沃于,CID(內(nèi)容標(biāo)識(shí)符)與它們通過(guò)加密哈希表示的內(nèi)容一一對(duì)應(yīng)。 具有相同內(nèi)容的兩個(gè)文件具有相同的CID(哈希)繁莹,并且就算只有一點(diǎn)點(diǎn)差異的兩個(gè)文件咨演,都具有不同的CID。 目錄也是如此零院。 每次更新文件或目錄的內(nèi)容時(shí)村刨,其CID都會(huì)更改。
當(dāng)你的根目錄為空并且使用ipfs.files.stat檢查其狀態(tài)時(shí)嵌牺,你會(huì)看到以下結(jié)果:
{
"hash": "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn",
"size": 0,
"cumulativeSize": 4,
"blocks": 0,
"type": "directory",
"withLocality": false
}
你現(xiàn)在添加了一個(gè)或多個(gè)文件會(huì)是什么樣子? 哪些字段現(xiàn)在應(yīng)該更改募疮?
Lesson 7 創(chuàng)建一個(gè)目錄
我們已經(jīng)學(xué)會(huì)了如何將文件添加到根目錄僻弹,但是我們?nèi)绾蝿?chuàng)建一個(gè)新目錄呢蹋绽? 同樣的,這與你在自己計(jì)算機(jī)上使用命令行的過(guò)程非常相似卸耘。
MFS的files.mkdir方法在指定路徑創(chuàng)建新目錄蚣抗。 例如,要將一個(gè)叫images目錄添加到根目錄(/)钝域,我們可以這樣做:
await ipfs.files.mkdir('/images')
可選項(xiàng)parents(默認(rèn)為false)指定了在給定的路徑中,如果有父目錄不存在的情況,是否要?jiǎng)?chuàng)建該父目錄赂毯。 這在上面的例子中不需要党涕,因?yàn)樾碌膇mages目錄是現(xiàn)有目錄(/)的直接子目錄。 但是膛堤,如果我們想要?jiǎng)?chuàng)建一個(gè)還不存在的新目錄肥荔,我們需要將parent的值顯式設(shè)置為true,如下所示:
await ipfs.files.mkdir('/my/beautiful/images', { parents : true })
注意中符!雖然創(chuàng)建不存在路徑的方法是類似的誉帅,但請(qǐng)注意我們使用files.mkdir的{parents : true}選項(xiàng),而不是files.write提供的{create : true}選項(xiàng)档插。
Lesson 8 移動(dòng)文件或目錄
MFS允許你使用files.mv方法在目錄之間移動(dòng)文件亚再,就像在本地計(jì)算機(jī)上一樣氛悬。
該方法如下所示:
await ipfs.files.mv(...from, to, [options])
from是你要移動(dòng)的內(nèi)容的源路徑(或路徑)。 to是目標(biāo)路徑忍级。
如果目標(biāo)路徑引用了尚不存在的上級(jí)路徑伪朽,則需要使用{parents : true}選項(xiàng),就像使用files.mkdir一樣朴肺。
你可以使用files.mv執(zhí)行許多不同的操作:
// move a single file into a directory
await ipfs.files.mv('/source-file.txt', '/destination-directory')
// move multiple files into a directory (note the two acceptable formats)
await ipfs.files.mv('/source-file-1.txt', '/source-file-2.txt', '/destination-directory')
await ipfs.files.mv(['/source-file-1.txt', '/source-file-2.txt'], '/destination-directory')
// move a directory into another directory
await ipfs.files.mv('/source-directory', '/destination-directory')
// overwrite the contents of a destination file with the contents of a source file
await ipfs.files.mv('/source-file.txt', '/destination-file.txt')
Lesson 9 復(fù)制文件或目錄
與files.mv不同戈稿,files.mv在將項(xiàng)目移動(dòng)到其目標(biāo)路徑后刪除其源路徑中的項(xiàng)目,files.cp方法允許你將文件或目錄復(fù)制到新位置需了,同時(shí)保留源位置的項(xiàng)目般甲。
該方法如下所示:
await ipfs.files.mv(...from, to, [options])
from是你要移動(dòng)的內(nèi)容的源路徑(或路徑)。 to是目標(biāo)路徑墓造。
如果目標(biāo)路徑引用了尚不存在的父目錄锚烦,則需要使用{parents : true}選項(xiàng),就像使用files.mkdir一樣谱煤。
你可以使用files.mv執(zhí)行許多不同的操作:
// move a single file into a directory
await ipfs.files.mv('/source-file.txt', '/destination-directory')
// move multiple files into a directory (note the two acceptable formats)
await ipfs.files.mv('/source-file-1.txt', '/source-file-2.txt', '/destination-directory')
await ipfs.files.mv(['/source-file-1.txt', '/source-file-2.txt'], '/destination-directory')
// move a directory into another directory
await ipfs.files.mv('/source-directory', '/destination-directory')
// overwrite the contents of a destination file with the contents of a source file
await ipfs.files.mv('/source-file.txt', '/destination-file.txt')
Lesson 10 瀏覽文件的內(nèi)容
MFS有一個(gè)files.read方法刘离,允許你在緩存中顯示文件的內(nèi)容睹栖。 這使我們可以輕松讀取.txt文件的內(nèi)容野来。
該方法采用以下格式:
await ipfs.files.read(path, [options])
path是要讀取的文件的路徑,它必須指向文件而不是目錄曼氛。
files.read方法返回一個(gè)Buffer舀患,可以使用 toString('utf8') 方法將其轉(zhuǎn)換為字符串。 例如:
let bufferedContents = await ipfs.files.read('/directory/some-file.txt') // a buffer
let contents = bufferedContents.toString('utf8') // a string
或者
let contents = (await ipfs.files.read('/directory/some-file.txt')).toString('utf8') // a string
// notice the parentheses around the entire await statement
當(dāng)你準(zhǔn)備在真機(jī)上嘗試此操作時(shí)餐抢,你需要注意files.read方法可能會(huì)因?yàn)樽x取的文件太大,從而導(dǎo)致大量?jī)?nèi)存被占用碳锈。 你可能希望通過(guò)files.readReadableStream或files.readPullStream方法來(lái)進(jìn)行進(jìn)行文件內(nèi)容的瀏覽欺抗。
Lession 11 刪除文件或目錄
MFS允許您使用files.rm方法刪除文件或目錄:
await ipfs.files.rm(...paths, [options])
paths是要?jiǎng)h除的一個(gè)或多個(gè)路徑绞呈。
默認(rèn)情況下,如果你嘗試刪除非空目錄报强,則將導(dǎo)致操作失敗秉溉。 要?jiǎng)h除目錄及其中包含的所有內(nèi)容碗誉,你需要使用{recursive : true}選項(xiàng)。
// remove a file
await ipfs.files.rm('/my/beautiful/file.txt')
// remove multiple files (note the two acceptable formats with or without [ ])
await ipfs.files.rm('/my/beautiful/file.txt', '/my/other/file.txt')
await ipfs.files.rm(['/my/beautiful/file.txt', '/my/other/file.txt'])
// remove a directory and its contents
await ipfs.files.rm('/my/beautiful/directory', { recursive: true })
// remove a directory only if it is empty
await ipfs.files.rm('/my/beautiful/directory')