Spaces:
Sleeping
Sleeping
Each collection of notes in Obsidian is known as a Vault. A Vault consists of a folder, and any sub-folders within it. | |
While your plugin can access the file system like any other Node.js application, the [[Reference/TypeScript API/Vault/Vault|Vault]] module aims to make it easier to work with files and folders within a Vault. | |
The following example recursively prints the paths of all Markdown files in a Vault: | |
```ts | |
const files = this.app.vault.getMarkdownFiles() | |
for (let i = 0; i < files.length; i++) { | |
console.log(files[i].path); | |
} | |
``` | |
> [!tip] | |
> If you want to list _all_ files, and not just Markdown documents, use [[getFiles|getFiles()]] instead. | |
## Read files | |
There are two methods for reading the content of a file: [[Reference/TypeScript API/Vault/read|read()]] and [[cachedRead|cachedRead()]]. | |
- If you only want to display the content to the user, then use `cachedRead()` to avoid reading the file from disk multiple times. | |
- If you want to read the content, change it, and then write it back to disk, then use `read()` to avoid potentially overwriting the file with a stale copy. | |
> [!info] | |
> The only difference between `cachedRead()` and `read()` is when the file was modified outside of Obsidian just before the plugin reads it. As soon as the file system notifies Obsidian that the file has changed from the outside, `cachedRead()` behaves _exactly_ like `read()`. Similarly, if you save the file within Obsidian, the read cache is flushed as well. | |
The following example reads the content of all Markdown files in the Vault and returns the average document size: | |
```ts | |
import { Notice, Plugin } from "obsidian"; | |
export default class ExamplePlugin extends Plugin { | |
async onload() { | |
this.addRibbonIcon("info", "Calculate average file length", async () => { | |
const fileLength = await this.averageFileLength(); | |
new Notice(`The average file length is ${fileLength} characters.`); | |
}); | |
} | |
async averageFileLength(): Promise<number> { | |
const { vault } = this.app; | |
const fileContents: string[] = await Promise.all( | |
vault.getMarkdownFiles().map((file) => vault.cachedRead(file)) | |
); | |
let totalLength = 0; | |
fileContents.forEach((content) => { | |
totalLength += content.length; | |
}); | |
return totalLength / fileContents.length; | |
} | |
} | |
``` | |
## Modify files | |
To write text content to an existing file, use [[modify|Vault.modify()]]. | |
```ts | |
function writeCurrentDate(vault: Vault, file: TFile): Promise<void> { | |
return vault.modify(file, `Today is ${new Intl.DateTimeFormat().format(new Date())}.`); | |
} | |
``` | |
If you want to modify a file based on its current content, use [[process|Vault.process()]] instead. The second argument is a callback that provides the current file content and returns the modified content. | |
```ts | |
// emojify replaces all occurrences of :) with 🙂. | |
function emojify(vault: Vault, file: TFile): Promise<string> { | |
return vault.process(file, (data) => { | |
return data.replace(":)", "🙂"); | |
}) | |
} | |
``` | |
`Vault.process()` is an abstraction on top of [[Reference/TypeScript API/Vault/read|Vault.read()]] and [[modify|Vault.modify()]] that guarantees that the file doesn't change between reading the current content and writing the updated content. Always prefer `Vault.process()` over `Vault.read()`/`Vault.modify()` to avoid unintentional loss of data. | |
### Asynchronous modifications | |
[[process|Vault.process()]] only supports synchronous modifications. If you need to modify a file asynchronously: | |
1. Read the file using [[cachedRead|Vault.cachedRead()]]. | |
2. Perform the async operations. | |
3. Update the file using [[Reference/TypeScript API/Vault/process|Vault.process()]]. | |
Remember to check that the `data` in the `process()` callback is the same as the data returned by `cachedRead()`. If they aren't the same, that means that the file was changed by a different process, and you may want to ask the user for confirmation, or try again. | |
## Delete files | |
There are two methods to delete a file, [[delete|delete()]], and [[trash|trash()]]. Which one you should use depends on if you want to allow the user to change their mind. | |
- `delete()` removes the file without a trace. | |
- `trash()` moves the file to the trash bin. | |
When you use `trash()`, you have the option to move the file to the system's trash bin, or to a local `.trash` folder at the root of the user's Vault. | |
## Is it a file or folder? | |
Some operations return or accept a [[TAbstractFile|TAbstractFile]] object, which can be either a file or a folder. Always check the concrete type of a `TAbstractFile` before you use it. | |
```ts | |
const folderOrFile = this.app.vault.getAbstractFileByPath("folderOrFile"); | |
if (folderOrFile instanceof TFile) { | |
console.log("It's a file!"); | |
} else if (folderOrFile instanceof TFolder) { | |
console.log("It's a folder!"); | |
} | |
``` | |