When we added files to IPFS in Lesson 3, we saw that the add
and addAll
methods returned path
, cid
, and size
values for each file, and that the path
and cid
string values were identical. To access each of these files later, we would need to know its CID. Did you notice that there was no filename provided?
The add
and addAll
methods do offer one slightly more human-friendly way to refer to files, but only if we use a special wrapWithDirectory
option to provide a path and filename for the added files. By doing this, we can create both CIDs for individual files, as we saw previously, and a CID for their containing directory, which can be used as part of a path to retrieve those files later by their filenames. This option will allow us to do interesting things with the ls
and get
methods, which we'll cover in upcoming lessons.
To use the wrapWithDirectory
option, you'll need to call the add
or addAll
method as follows:
ipfs.add(file, { wrapWithDirectory: true }) // add a single file
ipfs.addAll([file1, file2], { wrapWithDirectory: true }) // add multiple files via an array
When we used the add
method previously, we were able to pass in just a File
object from our browser. However, to wrap files with a directory, we need to also provide a path with the name we want for each file. To do this, we must replace the file
argument in the add
method with an object, structured like so:
{
path: 'file.txt', // a string for our desired path, including the filename
content: file // in our case, a File object
}
To add multiple files at once, we just need to pass an array of objects like the one shown above to the addAll
method. For example, we could add two cat pictures into a directory on our IPFS node like so:
ipfs.addAll([
{
path: 'adorable-kitty.jpg',
content: catPic1
},
{
path: 'cat-drinking-milk.jpg',
content: catPic2
}
], { wrapWithDirectory: true })
The add
or addAll
method will return information about each file we've added, as well as details about any directory it had to create in order to add the files with the correct paths. For example, in the case above, the addAll
method would return an array of three elements, one for each file added and another for the wrapping directory created.
Note that we could also have used the same addAll
method to create one or more subdirectories by adding them to the path of each file. For example:
ipfs.addAll([
{
path: 'cats/adorable-kitty.jpg',
content: catPic1
},
{
path: 'cats/cat-drinking-milk.jpg',
content: catPic2
},
{
path: 'dogs/dog-on-a-table.jpg',
content: dogPic1
}
], { wrapWithDirectory: true })
In this case we'd end up with six objects in our resulting array: one for each of the three files added, one for the new wrapping directory, one for the new cats
directory, and one for the new dogs
directory.
It's important to note that directories we create in this way do not behave as in a regular file system. If we've wrapped some files with a directory with using add
or addAll
, we can't simply add
new files to that directory. The contents of the directory we just created are final and immutable.
This is because of the way in which IPFS uses content addressing: different contents lead to a different cryptograhpic hash (Content Identifier), whether we're working with directories or files themselves.
But what if you forgot to add a file to a directory you just created? You'll have to call the addAll
method again with all the files you want wrapped with that directory, resulting in a new directory altogether with a new CID.
However, it's important to note that the file won't actually be stored multiple times if you do this. Unlike on your computer, where you can accidentally download the same file twice, store it in two different directories, and double your storage needs, the IPFS network inherently knows the contents of a file based on its CID, and can therefore keep only a single copy of the data that the CID refers to. It's only the reference to that data (the CID, which you can think of as a link or an addresses), which is stored in each of the directories you create. This efficiency is one of IPFS's key advantages.
Rather than thinking of directories created with { wrapWithDirectory: true }
as traditional file folders, it's more useful to think of them as naming shortcuts, as we'll see in upcoming lessons.
If you're looking for an experience that better mimics a traditional file system, you should explore the Mutable File System (MFS), a tool built into IPFS that lets you treat files like you normally would in a name-based filesystem — you can add, remove, move, and edit MFS files and have all the work of updating links and hashes taken care of for you. It's an abstraction that lets you deal with immutable data as if it were mutable.
Add multiple files to your IPFS node with addAll
, using { wrapWithDirectory: true }
to put them in a directory. Because you're targeting the top-level directory, not a subdirectory, the path
of each file should just be its name.
Hints:
map
or forEach
array methods to loop through each file in the files
array and access its name as file.name
.ipfs.addAll
method returns an Async Iterable
. You can use either the for await...of
loop or the function all
.Feeling stuck? We'd love to hear what's confusing so we can improve this lesson. Please share your questions and feedback.