Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion plugins/fs/android/src/main/java/FsPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions plugins/fs/src/file_segment.rs
Original file line number Diff line number Diff line change
@@ -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),
}
55 changes: 54 additions & 1 deletion plugins/fs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -313,7 +314,7 @@ impl OpenOptions {
mode
}
}

#[cfg(not(target_os = "android"))]
impl<R: Runtime> Fs<R> {
pub fn read_to_string<P: Into<FilePath>>(&self, path: P) -> std::io::Result<String> {
let mut s = String::new();
Expand Down Expand Up @@ -342,6 +343,58 @@ impl<R: Runtime> Fs<R> {
}
}

#[cfg(target_os = "android")]
use crate::file_segment::FileOrSegment;
#[cfg(target_os = "android")]
impl<R: Runtime> Fs<R> {
pub fn read_to_string<P: Into<FilePath>>(&self, path: P) -> std::io::Result<String> {
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<P: Into<FilePath>>(&self, path: P) -> std::io::Result<Vec<u8>> {
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 {
Expand Down
32 changes: 28 additions & 4 deletions plugins/fs/src/mobile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -34,11 +35,23 @@ pub fn init<R: Runtime, C: DeserializeOwned>(
pub struct Fs<R: Runtime>(PluginHandle<R>);

impl<R: Runtime> Fs<R> {
// need deprecated
pub fn open<P: Into<FilePath>>(
&self,
path: P,
opts: OpenOptions,
) -> std::io::Result<std::fs::File> {
match self.open_segment(path, opts)? {
FileOrSegment::File(f) => Ok(f),
FileOrSegment::Segment(fs) => Ok(fs.file),
}
}

pub fn open_segment<P: Into<FilePath>>(
&self,
path: P,
opts: OpenOptions,
) -> std::io::Result<FileOrSegment> {
match path.into() {
FilePath::Url(u) => self
.resolve_content_uri(u.to_string(), opts.android_mode())
Expand All @@ -62,7 +75,8 @@ impl<R: Runtime> Fs<R> {
)
})
} else {
std::fs::OpenOptions::from(opts).open(p)
let file = std::fs::OpenOptions::from(opts).open(p)?;
Ok(FileOrSegment::File(file))
}
}
}
Expand All @@ -73,7 +87,7 @@ impl<R: Runtime> Fs<R> {
&self,
uri: impl Into<String>,
mode: impl Into<String>,
) -> crate::Result<std::fs::File> {
) -> crate::Result<FileOrSegment> {
#[cfg(target_os = "android")]
{
let result = self.0.run_mobile_plugin::<GetFileDescriptorResponse>(
Expand All @@ -86,7 +100,17 @@ impl<R: Runtime> Fs<R> {
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!()
Expand Down
2 changes: 2 additions & 0 deletions plugins/fs/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ pub struct GetFileDescriptorPayload {
#[serde(rename_all = "camelCase")]
pub struct GetFileDescriptorResponse {
pub fd: Option<i32>,
pub offset: Option<u64>,
pub size: Option<u64>,
}