diff --git a/plugins/fs/android/src/main/java/FsPlugin.kt b/plugins/fs/android/src/main/java/FsPlugin.kt index 877fbf4a65..fdd986898d 100644 --- a/plugins/fs/android/src/main/java/FsPlugin.kt +++ b/plugins/fs/android/src/main/java/FsPlugin.kt @@ -45,8 +45,11 @@ class FsPlugin(private val activity: Activity): Plugin(activity) { if (args.uri.startsWith(app.tauri.TAURI_ASSETS_DIRECTORY_URI)) { val path = args.uri.substring(app.tauri.TAURI_ASSETS_DIRECTORY_URI.length) try { - val fd = activity.assets.openFd(path).parcelFileDescriptor?.detachFd() + val assetFd = activity.assets.openFd(path) + val fd = assetFd.parcelFileDescriptor?.detachFd() res.put("fd", fd) + res.put("offset", assetFd.startOffset) + res.put("size", assetFd.length) } catch (e: IOException) { // if the asset is compressed, we cannot open a file descriptor directly // so we copy it to the cache and get a fd from there diff --git a/plugins/fs/src/file_segment.rs b/plugins/fs/src/file_segment.rs new file mode 100644 index 0000000000..b2f8031138 --- /dev/null +++ b/plugins/fs/src/file_segment.rs @@ -0,0 +1,12 @@ +use std::fs::File; + +pub struct FileSegment{ + pub file: File, + pub offset: u64, + pub size: u64 +} + +pub enum FileOrSegment { + File(File), + Segment(FileSegment), +} \ No newline at end of file diff --git a/plugins/fs/src/lib.rs b/plugins/fs/src/lib.rs index bdc6b17099..3161a30074 100644 --- a/plugins/fs/src/lib.rs +++ b/plugins/fs/src/lib.rs @@ -32,6 +32,7 @@ mod models; mod scope; #[cfg(feature = "watch")] mod watcher; +mod file_segment; #[cfg(not(target_os = "android"))] pub use desktop::Fs; @@ -313,7 +314,7 @@ impl OpenOptions { mode } } - +#[cfg(not(target_os = "android"))] impl Fs { pub fn read_to_string>(&self, path: P) -> std::io::Result { let mut s = String::new(); @@ -342,6 +343,58 @@ impl Fs { } } +#[cfg(target_os = "android")] +use crate::file_segment::FileOrSegment; +#[cfg(target_os = "android")] +impl Fs { + pub fn read_to_string>(&self, path: P) -> std::io::Result { + use std::io::Seek; + let mut s = String::new(); + let f = self.open_segment( + path, + OpenOptions { + read: true, + ..Default::default() + }, + )?; + match f { + FileOrSegment::File(mut file) => { + file.read_to_string(&mut s)?; + }, + FileOrSegment::Segment(segment) => { + let mut handle = segment.file; + handle.seek(std::io::SeekFrom::Start(segment.offset))?; + let mut limited_reader = handle.take(segment.size); + limited_reader.read_to_string(&mut s)?; + } + } + Ok(s) + } + + pub fn read>(&self, path: P) -> std::io::Result> { + use std::io::Seek; + let mut buf = Vec::new(); + let f = self.open_segment( + path, + OpenOptions { + read: true, + ..Default::default() + }, + )?; + match f { + FileOrSegment::File(mut file) => { + file.read_to_end(&mut buf)?; + }, + FileOrSegment::Segment(segment) => { + let mut handle = segment.file; + handle.seek(std::io::SeekFrom::Start(segment.offset))?; + let mut limited_reader = handle.take(segment.size); + limited_reader.read_to_end(&mut buf)?; + } + } + Ok(buf) + } +} // implement ScopeObject here instead of in the scope module because it is also used on the build script // and we don't want to add tauri as a build dependency impl ScopeObject for scope::Entry { diff --git a/plugins/fs/src/mobile.rs b/plugins/fs/src/mobile.rs index 472f2c8a96..4230fef9a7 100644 --- a/plugins/fs/src/mobile.rs +++ b/plugins/fs/src/mobile.rs @@ -2,13 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use std::fs::File; use serde::de::DeserializeOwned; use tauri::{ plugin::{PluginApi, PluginHandle}, AppHandle, Runtime, }; -use crate::{models::*, FilePath, OpenOptions}; +use crate::{models::*, FilePath, OpenOptions, file_segment::{FileSegment, FileOrSegment}}; #[cfg(target_os = "android")] const PLUGIN_IDENTIFIER: &str = "com.plugin.fs"; @@ -34,11 +35,23 @@ pub fn init( pub struct Fs(PluginHandle); impl Fs { + // need deprecated pub fn open>( &self, path: P, opts: OpenOptions, ) -> std::io::Result { + match self.open_segment(path, opts)? { + FileOrSegment::File(f) => Ok(f), + FileOrSegment::Segment(fs) => Ok(fs.file), + } + } + + pub fn open_segment>( + &self, + path: P, + opts: OpenOptions, + ) -> std::io::Result { match path.into() { FilePath::Url(u) => self .resolve_content_uri(u.to_string(), opts.android_mode()) @@ -62,7 +75,8 @@ impl Fs { ) }) } else { - std::fs::OpenOptions::from(opts).open(p) + let file = std::fs::OpenOptions::from(opts).open(p)?; + Ok(FileOrSegment::File(file)) } } } @@ -73,7 +87,7 @@ impl Fs { &self, uri: impl Into, mode: impl Into, - ) -> crate::Result { + ) -> crate::Result { #[cfg(target_os = "android")] { let result = self.0.run_mobile_plugin::( @@ -86,7 +100,17 @@ impl Fs { if let Some(fd) = result.fd { Ok(unsafe { use std::os::fd::FromRawFd; - std::fs::File::from_raw_fd(fd) + let file: File = std::fs::File::from_raw_fd(fd); + match (result.offset, result.size) { + (Some(offset), Some(size)) => { + FileOrSegment::Segment(FileSegment { + file, + offset, + size, + }) + } + _ => FileOrSegment::File(file), + } }) } else { unimplemented!() diff --git a/plugins/fs/src/models.rs b/plugins/fs/src/models.rs index b9edc2cb21..032c367b10 100644 --- a/plugins/fs/src/models.rs +++ b/plugins/fs/src/models.rs @@ -15,4 +15,6 @@ pub struct GetFileDescriptorPayload { #[serde(rename_all = "camelCase")] pub struct GetFileDescriptorResponse { pub fd: Option, + pub offset: Option, + pub size: Option, }