tauri_plugin_android_fs/api/private_storage.rs
1use crate::*;
2
3
4/// API of file storage intended for the app’s use only.
5///
6/// # Examples
7/// ```
8/// fn example(app: &tauri::AppHandle) {
9/// use tauri_plugin_android_fs::AndroidFsExt as _;
10///
11/// let api = app.android_fs();
12/// let private_storage = api.private_storage();
13/// }
14/// ```
15pub struct PrivateStorage<'a, R: tauri::Runtime>(
16 #[allow(unused)]
17 pub(crate) &'a AndroidFs<R>
18);
19
20impl<'a, R: tauri::Runtime> PrivateStorage<'a, R> {
21
22 /// Get the absolute path of the specified directory.
23 /// App can fully manage entries within this directory without any permission via std::fs.
24 ///
25 /// These files will be deleted when the app is uninstalled and may also be deleted at the user’s initialising request.
26 /// When using [`PrivateDir::Cache`], the system will automatically delete files in this directory as disk space is needed elsewhere on the device.
27 ///
28 /// The returned path may change over time if the calling app is moved to an adopted storage device,
29 /// so only relative paths should be persisted.
30 ///
31 /// # Examples
32 /// ```no_run
33 /// use tauri_plugin_android_fs::{AndroidFs, AndroidFsExt, PrivateDir, PrivateStorage};
34 ///
35 /// fn example(app: tauri::AppHandle) {
36 /// let api = app.android_fs().private_storage();
37 ///
38 /// let dir_path = api.resolve_path(PrivateDir::Data).unwrap();
39 /// let file_path = dir_path.join("2025-2-12/data.txt");
40 ///
41 /// // Write file
42 /// std::fs::create_dir_all(file_path.parent().unwrap()).unwrap();
43 /// std::fs::write(&file_path, "aaaa").unwrap();
44 ///
45 /// // Read file
46 /// let _ = std::fs::read_to_string(&file_path).unwrap();
47 ///
48 /// // Remove file
49 /// std::fs::remove_file(&file_path).unwrap();
50 /// }
51 /// ```
52 ///
53 /// # Support
54 /// All Android version.
55 pub fn resolve_path(&self, dir: PrivateDir) -> crate::Result<std::path::PathBuf> {
56 on_android!({
57 impl_de!(struct Paths { data: String, cache: String });
58
59 static PATHS: std::sync::OnceLock<Paths> = std::sync::OnceLock::new();
60
61 if PATHS.get().is_none() {
62 let paths = self.0.api
63 .run_mobile_plugin::<Paths>("getPrivateBaseDirAbsolutePaths", "")?;
64
65 // The cell is guaranteed to contain a value when set returns
66 let _ = PATHS.set(paths);
67 }
68
69 let paths = PATHS.get().unwrap();
70
71 Ok(match dir {
72 PrivateDir::Data => std::path::PathBuf::from(paths.data.to_owned()),
73 PrivateDir::Cache => std::path::PathBuf::from(paths.cache.to_owned()),
74 })
75 })
76 }
77
78 /// Get the absolute path of the specified relative path and base directory.
79 /// App can fully manage entries of this path without any permission via std::fs.
80 ///
81 /// See [`PrivateStorage::resolve_path`] for details.
82 ///
83 /// # Support
84 /// All Android version.
85 pub fn resolve_path_with(
86 &self,
87 dir: PrivateDir,
88 relative_path: impl AsRef<str>
89 ) -> crate::Result<std::path::PathBuf> {
90
91 on_android!({
92 let relative_path = relative_path.as_ref().trim_start_matches('/');
93 let path = self.resolve_path(dir)?.join(relative_path);
94 Ok(path)
95 })
96 }
97
98 pub fn resolve_uri(&self, dir: PrivateDir) -> crate::Result<FileUri> {
99 on_android!({
100 self.resolve_path(dir).map(Into::into)
101 })
102 }
103
104 pub fn resolve_uri_with(&self, dir: PrivateDir, relative_path: impl AsRef<str>) -> crate::Result<FileUri> {
105 on_android!({
106 self.resolve_path_with(dir, relative_path).map(Into::into)
107 })
108 }
109
110 /// Writes a slice as the entire contents of a file.
111 ///
112 /// This function will create a file if it does not exist, and will entirely replace its contents if it does.
113 /// Recursively create parent directories if they are missing.
114 ///
115 /// This internally uses [`PrivateStorage::resolve_path`] , [`std::fs::create_dir_all`], and [`std::fs::write`].
116 /// See [`PrivateStorage::resolve_path`] for details.
117 ///
118 /// # Support
119 /// All Android version.
120 pub fn write(
121 &self,
122 base_dir: PrivateDir,
123 relative_path: impl AsRef<str>,
124 contents: impl AsRef<[u8]>
125 ) -> crate::Result<()> {
126
127 on_android!({
128 let path = self.resolve_path_with(base_dir, relative_path)?;
129
130 if let Some(parent_dir) = path.parent() {
131 std::fs::create_dir_all(parent_dir)?;
132 }
133
134 std::fs::write(path, contents)?;
135 Ok(())
136 })
137 }
138
139 /// Open a file in read-only mode.
140 ///
141 /// If you only need to read the entire file contents, consider using [`PrivateStorage::read`] or [`PrivateStorage::read_to_string`] instead.
142 ///
143 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::File::open`].
144 /// See [`PrivateStorage::resolve_path`] for details.
145 ///
146 /// # Support
147 /// All Android version.
148 pub fn open_file(
149 &self,
150 base_dir: PrivateDir,
151 relative_path: impl AsRef<str>,
152 ) -> crate::Result<std::fs::File> {
153
154 on_android!({
155 let path = self.resolve_path_with(base_dir, relative_path)?;
156 Ok(std::fs::File::open(path)?)
157 })
158 }
159
160 /// Opens a file in write-only mode.
161 /// This function will create a file if it does not exist, and will truncate it if it does.
162 ///
163 /// If you only need to write the contents, consider using [`PrivateStorage::write`] instead.
164 ///
165 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::File::create`].
166 /// See [`PrivateStorage::resolve_path`] for details.
167 ///
168 /// # Support
169 /// All Android version.
170 pub fn create_file(
171 &self,
172 base_dir: PrivateDir,
173 relative_path: impl AsRef<str>,
174 ) -> crate::Result<std::fs::File> {
175
176 on_android!({
177 let path = self.resolve_path_with(base_dir, relative_path)?;
178 Ok(std::fs::File::create(path)?)
179 })
180 }
181
182 /// Creates a new file in read-write mode; error if the file exists.
183 ///
184 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::File::create_new`].
185 /// See [`PrivateStorage::resolve_path`] for details.
186 ///
187 /// # Support
188 /// All Android version.
189 pub fn create_new_file(
190 &self,
191 base_dir: PrivateDir,
192 relative_path: impl AsRef<str>,
193 ) -> crate::Result<std::fs::File> {
194
195 on_android!({
196 let path = self.resolve_path_with(base_dir, relative_path)?;
197 Ok(std::fs::File::create_new(path)?)
198 })
199 }
200
201 /// Reads the entire contents of a file into a bytes vector.
202 ///
203 /// If you need [`std::fs::File`], use [`PrivateStorage::open_file`] insted.
204 ///
205 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read`].
206 /// See [`PrivateStorage::resolve_path`] for details.
207 ///
208 /// # Support
209 /// All Android version.
210 pub fn read(
211 &self,
212 base_dir: PrivateDir,
213 relative_path: impl AsRef<str>,
214 ) -> crate::Result<Vec<u8>> {
215
216 on_android!({
217 let path = self.resolve_path_with(base_dir, relative_path)?;
218 Ok(std::fs::read(path)?)
219 })
220 }
221
222 /// Reads the entire contents of a file into a string.
223 ///
224 /// If you need [`std::fs::File`], use [`PrivateStorage::open_file`] insted.
225 ///
226 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read_to_string`].
227 /// See [`PrivateStorage::resolve_path`] for details.
228 ///
229 /// # Support
230 /// All Android version.
231 pub fn read_to_string(
232 &self,
233 base_dir: PrivateDir,
234 relative_path: impl AsRef<str>,
235 ) -> crate::Result<String> {
236
237 on_android!({
238 let path = self.resolve_path_with(base_dir, relative_path)?;
239 Ok(std::fs::read_to_string(path)?)
240 })
241 }
242
243 /// Returns an iterator over the entries within a directory.
244 ///
245 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::read_dir`].
246 /// See [`PrivateStorage::resolve_path`] for details.
247 ///
248 /// # Support
249 /// All Android version.
250 pub fn read_dir(
251 &self,
252 base_dir: PrivateDir,
253 relative_path: Option<&str>,
254 ) -> crate::Result<std::fs::ReadDir> {
255
256 on_android!({
257 let path = match relative_path {
258 Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
259 None => self.resolve_path(base_dir)?,
260 };
261
262 Ok(std::fs::read_dir(path)?)
263 })
264 }
265
266 /// Removes a file from the filesystem.
267 ///
268 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_file`].
269 /// See [`PrivateStorage::resolve_path`] for details.
270 ///
271 /// # Support
272 /// All Android version.
273 pub fn remove_file(
274 &self,
275 base_dir: PrivateDir,
276 relative_path: impl AsRef<str>,
277 ) -> crate::Result<()> {
278
279 on_android!({
280 let path = self.resolve_path_with(base_dir, relative_path)?;
281 Ok(std::fs::remove_file(path)?)
282 })
283 }
284
285 /// Removes an empty directory.
286 /// If you want to remove a directory that is not empty, as well as all of its contents recursively, consider using [`PrivateStorage::remove_dir_all`] instead.
287 ///
288 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_dir`].
289 /// See [`PrivateStorage::resolve_path`] for details.
290 ///
291 /// # Support
292 /// All Android version.
293 pub fn remove_dir(
294 &self,
295 base_dir: PrivateDir,
296 relative_path: Option<&str>,
297 ) -> crate::Result<()> {
298
299 on_android!({
300 let path = match relative_path {
301 Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
302 None => self.resolve_path(base_dir)?,
303 };
304
305 std::fs::remove_dir(path)?;
306 Ok(())
307 })
308 }
309
310 /// Removes a directory at this path, after removing all its contents. Use carefully!
311 ///
312 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::remove_dir_all`].
313 /// See [`PrivateStorage::resolve_path`] for details.
314 ///
315 /// # Support
316 /// All Android version.
317 pub fn remove_dir_all(
318 &self,
319 base_dir: PrivateDir,
320 relative_path: Option<&str>,
321 ) -> crate::Result<()> {
322
323 on_android!({
324 let path = match relative_path {
325 Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
326 None => self.resolve_path(base_dir)?,
327 };
328
329 std::fs::remove_dir_all(path)?;
330 Ok(())
331 })
332 }
333
334 /// Returns Ok(true) if the path points at an existing entity.
335 ///
336 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::exists`].
337 /// See [`PrivateStorage::resolve_path`] for details.
338 ///
339 /// # Support
340 /// All Android version.
341 pub fn exists(
342 &self,
343 base_dir: PrivateDir,
344 relative_path: impl AsRef<str>
345 ) -> crate::Result<bool> {
346
347 on_android!({
348 let path = self.resolve_path_with(base_dir, relative_path)?;
349 Ok(std::fs::exists(path)?)
350 })
351 }
352
353 /// Queries the file system to get information about a file, directory.
354 ///
355 /// This internally uses [`PrivateStorage::resolve_path`] and [`std::fs::metadata`].
356 /// See [`PrivateStorage::resolve_path`] for details.
357 ///
358 /// # Support
359 /// All Android version.
360 pub fn metadata(
361 &self,
362 base_dir: PrivateDir,
363 relative_path: Option<&str>,
364 ) -> crate::Result<std::fs::Metadata> {
365
366 on_android!({
367 let path = match relative_path {
368 Some(relative_path) => self.resolve_path_with(base_dir, relative_path)?,
369 None => self.resolve_path(base_dir)?,
370 };
371
372 Ok(std::fs::metadata(path)?)
373 })
374 }
375
376
377 #[allow(unused)]
378 pub(crate) fn create_new_tmp_file(&self) -> crate::Result<(std::fs::File, std::path::PathBuf)> {
379 on_android!({
380 let tmp_file_path = {
381 use std::sync::atomic::{AtomicUsize, Ordering};
382
383 static COUNTER: AtomicUsize = AtomicUsize::new(0);
384 let id = COUNTER.fetch_add(1, Ordering::Relaxed);
385
386 let tmp_dir_path = self.resolve_tmp_dir()?;
387 let _ = std::fs::create_dir_all(&tmp_dir_path);
388
389 tmp_dir_path.join(format!("tmp file {id}"))
390 };
391
392 let tmp_file = std::fs::File::create_new(&tmp_file_path)?;
393
394 Ok((tmp_file, tmp_file_path))
395 })
396 }
397
398 #[allow(unused)]
399 pub(crate) fn remove_all_tmp_files(&self) -> crate::Result<()> {
400 on_android!({
401 match std::fs::remove_dir_all(self.resolve_tmp_dir()?) {
402 Ok(_) => Ok(()),
403 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
404 Err(e) => Err(e.into()),
405 }
406 })
407 }
408
409 #[allow(unused)]
410 pub(crate) fn resolve_tmp_dir(&self) -> crate::Result<std::path::PathBuf> {
411 on_android!({
412 const TMP_DIR_RELATIVE_PATH: &str = "pluginAndroidFs-tmpDir-01K486FKQ2BZSBGFD34RFH9FWJ";
413
414 self.resolve_path_with(PrivateDir::Cache, TMP_DIR_RELATIVE_PATH)
415 })
416 }
417}