diff --git a/.vscode/settings.json b/.vscode/settings.json
index e66e92071..be713d381 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,5 +3,8 @@
"prettier.documentSelectors": ["**/*.astro"],
"[astro]": {
"editor.defaultFormatter": "astro-build.astro-vscode"
+ },
+ "[mdx]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
diff --git a/src/content/docs/develop/resources.mdx b/src/content/docs/develop/resources.mdx
index 202ee0b4c..794ca030d 100644
--- a/src/content/docs/develop/resources.mdx
+++ b/src/content/docs/develop/resources.mdx
@@ -3,29 +3,79 @@ title: Embedding Additional Files
i18nReady: true
---
+import { Tabs, TabItem } from '@astrojs/starlight/components';
+
You may need to include additional files in your application bundle that aren't part of your frontend (your `frontendDist`) directly or which are too big to be inlined into the binary. We call these files `resources`.
-To bundle the files of your choice, you can add the `resources` property to the `bundle` object in your `tauri.conf.json` file.
+## Configuration
-See more about `tauri.conf.json` configuration [here][tauri.bundle].
+To bundle the files of your choice, add the `resources` property to the `bundle` object in your `tauri.conf.json` file.
-`resources` expects a list of strings targeting files or directories either with absolute or relative paths. It supports glob patterns in case you need to include multiple files from a directory.
+To include a list of files:
-Here is a sample to illustrate the configuration. This is not a complete `tauri.conf.json` file:
+
+
```json title=tauri.conf.json
{
"bundle": {
"resources": [
+ "./path/to/some-file.txt",
"/absolute/path/to/textfile.txt",
- "relative/path/to/jsonfile.json",
- "resources/**/*"
+ "../relative/path/to/jsonfile.json",
+ "some-folder/",
+ "resources/**/*.md"
]
}
}
```
-Alternatively the `resources` config also accepts a map object if you want to change where the files will be copied to. Here is a sample that shows how to include files from different sources into the same `resources` folder:
+
+
+
+```json title=tauri.conf.json5
+{
+ "bundle": {
+ "resources": [
+ // Will be placed to `$RESOURCE/path/to/some-file.txt`
+ "./path/to/some-file.txt",
+
+ // The root in an abosolute path will be replaced by `_root_`,
+ // so `textfile.txt` will be placed to `$RESOURCE/_root_/absolute/path/to/textfile.txt`
+ "/absolute/path/to/textfile.txt",
+
+ // `..` in a relative path will be replaced by `_up_`,
+ // so `jsonfile.json` will be placed to `$RESOURCE/_up_/relative/path/to/textfile.txt`,
+ "../relative/path/to/jsonfile.json",
+
+ // If the path is a directory, the entire directory will be copied to the `$RESOURCE` directory,
+ // preserving the original structures, for example:
+ // - `some-folder/file.txt` -> `$RESOURCE/some-folder/file.txt`
+ // - `some-folder/another-folder/config.json` -> `$RESOURCE/some-folder/another-folder/config.json`
+ // This is the same as `some-folder/**/*`
+ "some-folder/",
+
+ // You can also include multiple files at once through glob patterns.
+ // All the `.md` files inside `resources` will be placed to `$RESOURCE/resources/`,
+ // preserving their original directory structures, for example:
+ // - `resources/index.md` -> `$RESOURCE/resources/index.md`
+ // - `resources/docs/setup.md` -> `$RESOURCE/resources/docs/setup.md`
+ "resources/**/*.md"
+ ]
+ }
+}
+```
+
+
+
+
+The bundled files will be in `$RESOURCES/` with the original directory structure preserved,
+for example: `./path/to/some-file.txt` -> `$RESOURCE/path/to/some-file.txt`
+
+To fine control where the files will get copied to, use a map instead:
+
+
+
```json title=tauri.conf.json
{
@@ -33,19 +83,50 @@ Alternatively the `resources` config also accepts a map object if you want to ch
"resources": {
"/absolute/path/to/textfile.txt": "resources/textfile.txt",
"relative/path/to/jsonfile.json": "resources/jsonfile.json",
- "resources/**/*": "resources/"
+ "resources/": "",
+ "docs/**/*md": "website-docs/"
}
}
}
```
-:::note
+
+
-In Tauri's [permission system](/reference/acl/capability/), absolute paths and paths containing parent components (`../`) can only be allowed via `"$RESOURCE/**"`. Relative paths like `"path/to/file.txt"` can be allowed explicitly via `"$RESOURCE/path/to/file.txt"`.
+```json title=tauri.conf.json5
+{
+ "bundle": {
+ "resources": {
+ // `textfile.txt` will be placed to `$RESOURCE/resources/textfile.txt`
+ "/absolute/path/to/textfile.txt": "resources/textfile.txt",
-:::
+ // `jsonfile.json` will be placed to `$RESOURCE/resources/jsonfile.json`
+ "relative/path/to/jsonfile.json": "resources/jsonfile.json",
-## Source path syntax
+ // Copy the entire directory to `$RESOURCE`, preserving the original structures,
+ // the target is "" which means it will be placed directly in the resource directory `$RESOURCE`, for example:
+ // - `resources/file.txt` -> `$RESOURCE/file.txt`
+ // - `resources/some-folder/config.json` -> `$RESOURCE/some-folder/config.json`
+ "resources/": "",
+
+ // When using glob patterns, the behavior is different from the list one,
+ // all the matching files will be placed to the target directory without preserving the original file structures
+ // for example:
+ // - `docs/index.md` -> `$RESOURCE/website-docs/index.md`
+ // - `docs/plugins/setup.md` -> `$RESOURCE/website-docs/setup.md`
+ "docs/**/*md": "website-docs/"
+ }
+ }
+}
+```
+
+
+
+
+To learn about where `$RESOURCE` resolves to on each platforms, see the documentation of [`resource_dir`]
+
+
+Source path syntax
In the following explanations "target resource directory" is either the value after the colon in the object notation, or a reconstruction of the original file paths in the array notation.
@@ -56,19 +137,111 @@ In the following explanations "target resource directory" is either the value af
- `"dir/**/*"`: copies all files in the `dir` directory _recursively_ (all files in `dir/` and all files in all sub-directories) into the target resource directory.
- `"dir/**/**`: throws an error because `**` only matches directories and therefore no files can be found.
-## Accessing files in Rust
+
-In this example we want to bundle additional i18n json files that look like this:
+## Resolve resource file paths
-```json title=de.json
+To resolve the path for a resource file, instead of manually calculating the path, use the following APIs
+
+
+
+
+On the Rust side, you need an instance of the [`PathResolver`] which you can get from [`App`] and [`AppHandle`],
+then call [`PathResolver::resolve`]:
+
+```rust
+tauri::Builder::default()
+ .setup(|app| {
+ let resource_path = app.path().resolve("lang/de.json", BaseDirectory::Resource)?;
+ Ok(())
+ })
+```
+
+To use it in a command:
+
+```rust
+#[tauri::command]
+fn hello(handle: tauri::AppHandle) {
+ let resource_path = handle.path().resolve("lang/de.json", BaseDirectory::Resource)?;
+}
+```
+
+
+
+
+To resolve the path in JavaScript, use [`resolveResource`]:
+
+```javascript
+import { resolveResource } from '@tauri-apps/api/path';
+const resourcePath = await resolveResource('lang/de.json');
+```
+
+
+
+
+### Path syntax
+
+The path in the API calls can be either a normal relative path like `folder/json_file.json` that resolves to `$RESOURCE/folder/json_file.json`,
+or a paths like `../relative/folder/toml_file.toml` that resolves to `$RESOURCE/_up_/relative/folder/toml_file.toml`,
+these APIs use the same rules as you write `tauri.conf.json > bundle > resources`, for example:
+
+```json title=tauri.conf.json
+{
+ "bundle": {
+ "resources": ["folder/json_file.json", "../relative/folder/toml_file.toml"]
+ }
+}
+```
+
+```rust
+let json_path = app.path().resolve("folder/json_file.json", BaseDirectory::Resource)?;
+let toml_path = app.path().resolve("../relative/folder/toml_file.toml", BaseDirectory::Resource)?;
+```
+
+### Android
+
+Currently the resources are stored in the APK as assets so the return value of those APIs are not normal file system paths,
+we use a special URI prefix `asset://localhost/` here that can be used with the [`fs` plugin],
+with that, you can read the files through [`FsExt::fs`] like this:
+
+```rust
+let resource_path = app.path().resolve("lang/de.json", BaseDirectory::Resource).unwrap();
+let json = app.fs().read_to_string(&resource_path);
+```
+
+If you want or must have the resource files to be on a real file system, copy the contents out manually through the [`fs` plugin]
+
+## Reading resource files
+
+In this example we want to bundle additional i18n json files like this:
+
+```
+.
+├── src-tauri/
+│ ├── tauri.conf.json
+│ ├── lang/
+│ │ ├── de.json
+│ │ └── en.json
+│ └── ...
+└── ...
+```
+
+```json title=tauri.conf.json
+{
+ "bundle": {
+ "resources": ["lang/*"]
+ }
+}
+```
+
+```json title=lang/de.json
{
"hello": "Guten Tag!",
"bye": "Auf Wiedersehen!"
}
```
-In this case, we store these files in a `lang` directory next to the `tauri.conf.json`.
-For this we add `"lang/*"` to `resources` as shown above.
+### Rust
On the Rust side, you need an instance of the [`PathResolver`] which you can get from [`App`] and [`AppHandle`]:
@@ -79,8 +252,11 @@ tauri::Builder::default()
// `tauri.conf.json > bundle > resources`
let resource_path = app.path().resolve("lang/de.json", BaseDirectory::Resource)?;
- let file = std::fs::File::open(&resource_path).unwrap();
- let lang_de: serde_json::Value = serde_json::from_reader(file).unwrap();
+ let json = std::fs::read_to_string(&resource_path).unwrap();
+ // Or when dealing with Android, use the file system plugin instead
+ // let json = app.fs().read_to_string(&resource_path);
+
+ let lang_de: serde_json::Value = serde_json::from_str(json).unwrap();
// This will print 'Guten Tag!' to the terminal
println!("{}", lang_de.get("hello").unwrap());
@@ -94,18 +270,21 @@ tauri::Builder::default()
fn hello(handle: tauri::AppHandle) -> String {
let resource_path = handle.path().resolve("lang/de.json", BaseDirectory::Resource)?;
- let file = std::fs::File::open(&resource_path).unwrap();
- let lang_de: serde_json::Value = serde_json::from_reader(file).unwrap();
+ let json = std::fs::read_to_string(&resource_path).unwrap();
+ // Or when dealing with Android, use the file system plugin instead
+ // let json = handle.fs().read_to_string(&resource_path);
+
+ let lang_de: serde_json::Value = serde_json::from_str(json).unwrap();
lang_de.get("hello").unwrap()
}
```
-## Accessing files in JavaScript
+### JavaScript
-For the JavaScript side, you can either use a command like the one above and call it through `await invoke('hello')` or access the files using the [`plugin-fs`]
+For the JavaScript side, you can either use a command like the one above and call it through `await invoke('hello')` or access the files using the [`fs` plugin].
-When using the [`plugin-fs`], addition from the [basic setup], you'll also need to configure the access control list to enable any [`plugin-fs`] APIs you will need as well as permissions to access the `$RESOURCE` folder:
+When using the [`fs` plugin], in addition to the [basic setup], you'll also need to configure the access control list to enable any plugin APIs you need as well as the permissions to access the `$RESOURCE` folder:
```json title=src-tauri/capabilities/default.json ins={8-9}
{
@@ -135,11 +314,78 @@ const langDe = JSON.parse(await readTextFile(resourcePath));
console.log(langDe.hello); // This will print 'Guten Tag!' to the devtools console
```
-[tauri.bundle]: /reference/config/#bundleconfig
+## Permissions
+
+Since we replace `../` to `_up_` in relative paths and the root to `_root_` in abosolute paths when using a list,
+those files will be in sub folders inside the resource directory,
+to allow those paths in Tauri's [permission system](/security/capabilities/),
+use `$RESOURCE/**/*` to allow recursive access to those files
+
+### Examples
+
+With a file bundled like this:
+
+```json title=tauri.conf.json
+{
+ "bundle": {
+ "resources": ["../relative/path/to/jsonfile.json"]
+ }
+}
+```
+
+To use it with the [`fs` plugin]:
+
+```json title=src-tauri/capabilities/default.json ins={8-15}
+{
+ "$schema": "../gen/schemas/desktop-schema.json",
+ "identifier": "main-capability",
+ "description": "Capability for the main window",
+ "windows": ["main"],
+ "permissions": [
+ "core:default",
+ "fs:allow-stat",
+ "fs:allow-read-text-file",
+ "fs:allow-resource-read-recursive",
+ {
+ "identifier": "fs:scope",
+ "allow": ["$RESOURCE/**/*"],
+ "deny": ["$RESOURCE/secret.txt"]
+ }
+ ]
+}
+```
+
+To use it with the [`opener` plugin]:
+
+```json title=src-tauri/capabilities/default.json ins={8-15}
+{
+ "$schema": "../gen/schemas/desktop-schema.json",
+ "identifier": "main-capability",
+ "description": "Capability for the main window",
+ "windows": ["main"],
+ "permissions": [
+ "core:default",
+ {
+ "identifier": "opener:allow-open-path",
+ "allow": [
+ {
+ "path": "$RESOURCE/**/*"
+ }
+ ]
+ }
+ ]
+}
+```
+
+[`resource_dir`]: https://docs.rs/tauri/latest/tauri/path/struct.PathResolver.html#method.resource_dir
[`pathresolver`]: https://docs.rs/tauri/latest/tauri/path/struct.PathResolver.html
+[`PathResolver::resolve`]: https://docs.rs/tauri/latest/tauri/path/struct.PathResolver.html#method.resolve
+[`resolveResource`]: https://tauri.app/reference/javascript/api/namespacepath/#resolveresource
[`app`]: https://docs.rs/tauri/latest/tauri/struct.App.html
[`apphandle`]: https://docs.rs/tauri/latest/tauri/struct.AppHandle.html
-[`plugin-fs`]: /plugin/file-system/
+[`fs` plugin]: /plugin/file-system/
+[`FsExt::fs`]: https://docs.rs/tauri-plugin-fs/latest/tauri_plugin_fs/trait.FsExt.html#tymethod.fs
[basic setup]: /plugin/file-system/#setup
[Scope Permissions]: /plugin/file-system/#scopes
[scopes]: /plugin/file-system/#scopes
+[`opener` plugin]: /plugin/opener/