Skip to main content

Storage

Struct Storage 

Source
pub struct Storage { /* private fields */ }
Expand description

A storage location on an MTP device.

Storage holds an Arc<MtpDeviceInner> so it can outlive the original MtpDevice and be used from multiple tasks.

Implementations§

Source§

impl Storage

Source

pub fn id(&self) -> StorageId

Source

pub fn info(&self) -> &StorageInfo

Storage information (cached, call refresh() to update).

Source

pub async fn refresh(&mut self) -> Result<(), Error>

Refresh storage info from device (updates free space, etc.).

Source

pub async fn list_objects( &self, parent: Option<ObjectHandle>, ) -> Result<Vec<ObjectInfo>, Error>

List objects in a folder (None = root), returning all results at once.

For progress reporting during large listings, use list_objects_stream() instead.

This method handles various device quirks:

  • Root listing tries parent=0xFFFFFFFF first (fast path for Android, Kindle, etc.)
  • Falls back to parent=0 only when the device rejects 0xFFFFFFFF
  • Samsung devices: return InvalidObjectHandle for parent=0, so we fall back to recursive
  • Fuji devices: return all objects for root, so we filter by parent handle
Source

pub async fn list_objects_with_cancel( &self, parent: Option<ObjectHandle>, cancel: Option<&CancelToken>, ) -> Result<Vec<ObjectInfo>, Error>

Like list_objects, but takes a cooperative cancellation token.

When cancel is Some(&token) and the token has been cancelled, the call bails between per-handle fetches with Err(Error::Cancelled). Useful for large folders (1k+ entries on Android), where the GetObjectInfo per-handle loop dominates wall-clock time.

Pass None for backwards-compatible behavior identical to list_objects.

Source

pub async fn list_objects_stream( &self, parent: Option<ObjectHandle>, ) -> Result<ObjectListing, Error>

List objects in a folder as a streaming ObjectListing.

Returns immediately after GetObjectHandles completes. The total count is then known via ObjectListing::total(), and each call to ObjectListing::next() fetches one object’s metadata from USB.

For root listings (parent=None), tries parent=0xFFFFFFFF first — this returns only root-level handles on Android, Kindle, and many other devices. Falls back to parent=0 only when the device rejects 0xFFFFFFFF with an error. An empty result from 0xFFFFFFFF is treated as an empty storage, not as a reason to fall back.

This enables progress reporting (e.g., “Loading 42 of 500…”) during what would otherwise be a single blocking list_objects() call.

Handles the same device quirks as list_objects().

§Example
use mtp_rs::mtp::MtpDevice;

let mut listing = storage.list_objects_stream(None).await?;
println!("Found {} items", listing.total());

while let Some(result) = listing.next().await {
    let info = result?;
    println!("[{}/{}] {}", listing.fetched(), listing.total(), info.filename);
}
Source

pub async fn list_objects_stream_with_cancel( &self, parent: Option<ObjectHandle>, cancel: Option<&CancelToken>, ) -> Result<ObjectListing, Error>

Like list_objects_stream, but the returned ObjectListing carries an optional CancelToken. Every call to ObjectListing::next checks the token before issuing the next GetObjectInfo USB roundtrip, so a flipped token bails within one roundtrip’s worth of latency instead of running to completion.

Source

pub async fn list_objects_recursive( &self, parent: Option<ObjectHandle>, ) -> Result<Vec<ObjectInfo>, Error>

List objects recursively.

This method automatically detects Android devices and uses manual traversal for them, since Android’s MTP implementation doesn’t support the native ObjectHandle::ALL recursive listing.

For non-Android devices, it tries native recursive listing first and falls back to manual traversal if the results look incomplete.

Source

pub async fn list_objects_recursive_native( &self, parent: Option<ObjectHandle>, ) -> Result<Vec<ObjectInfo>, Error>

List objects recursively using native MTP recursive listing.

Source

pub async fn list_objects_recursive_manual( &self, parent: Option<ObjectHandle>, ) -> Result<Vec<ObjectInfo>, Error>

List objects recursively using manual folder traversal.

Source

pub async fn get_object_info( &self, handle: ObjectHandle, ) -> Result<ObjectInfo, Error>

Get object metadata by handle.

Files larger than 4 GB have their u64 size auto-resolved via GetObjectPropValue(ObjectSize); the standard ObjectInfo dataset only encodes a u32 size which saturates at 4 GB - 1.

Source

pub async fn download(&self, handle: ObjectHandle) -> Result<Vec<u8>, Error>

Download a file and return all bytes.

For small to medium files where you want all the data in memory. For large files or streaming to disk, use download_stream().

Source

pub async fn download_partial( &self, handle: ObjectHandle, offset: u64, size: u32, ) -> Result<Vec<u8>, Error>

Download a partial file (byte range).

Uses the standard GetPartialObject operation, which has a 32-bit offset. Offsets beyond 4 GB will be silently truncated — for files larger than 4 GB, use download_partial_64() instead.

Source

pub async fn download_partial_64( &self, handle: ObjectHandle, offset: u64, size: u32, ) -> Result<Vec<u8>, Error>

Download a partial file (byte range) with 64-bit offset support.

Uses the Android/MTP extension GetPartialObject64, which supports offsets beyond 4 GB. Only works on devices that advertise support for it (most modern Android devices do); others return OperationNotSupported.

Source

pub async fn download_thumbnail( &self, handle: ObjectHandle, ) -> Result<Vec<u8>, Error>

Source

pub async fn download_stream( &self, handle: ObjectHandle, ) -> Result<FileDownload, Error>

Download a file as a stream (true USB streaming).

Unlike download(), this method yields data chunks directly from USB as they arrive, without buffering the entire file in memory. Ideal for large files or when piping data to disk.

§Important

The MTP session is locked while the download is active. You must either consume the entire download or call FileDownload::cancel() before dropping it.

§Example
use mtp_rs::mtp::MtpDevice;
use mtp_rs::ObjectHandle;
use tokio::io::AsyncWriteExt;

let mut download = storage.download_stream(handle).await?;
println!("Downloading {} bytes...", download.size());

let mut file = tokio::fs::File::create("output.bin").await?;
while let Some(chunk) = download.next_chunk().await {
    let bytes = chunk?;
    file.write_all(&bytes).await?;
    println!("Progress: {:.1}%", download.progress() * 100.0);
}
Source

pub async fn upload<S>( &self, parent: Option<ObjectHandle>, info: NewObjectInfo, data: S, ) -> Result<ObjectHandle, UploadError>
where S: Stream<Item = Result<Bytes, Error>> + Unpin + Send,

Upload a file from a stream.

The data streams directly to USB in chunks; the protocol only needs the total size upfront (provided via info), not the whole file in memory.

§Arguments
  • parent - Parent folder handle (None for root)
  • info - Object metadata including filename and size
  • data - Stream of data chunks to upload
§Errors

Returns UploadError on failure. PTP uploads are two-phase: SendObjectInfo creates the object on the device (yielding a handle), then SendObject streams the bytes. If the data phase fails, the device is left holding a partial (possibly empty or truncated) object, and UploadError::partial carries its handle so you can delete it or retry the data phase to resume. The library does not auto-delete it. See UploadError for the full contract.

Source

pub async fn upload_with_progress<S, F>( &self, parent: Option<ObjectHandle>, info: NewObjectInfo, data: S, on_progress: F, ) -> Result<ObjectHandle, UploadError>
where S: Stream<Item = Result<Bytes, Error>> + Unpin + Send, F: FnMut(Progress) -> ControlFlow<()> + Send,

Upload a file with progress callback.

Progress is reported as data is read from the stream. Return ControlFlow::Break(()) from the callback to cancel the upload.

§Errors

Returns UploadError on failure or cancellation. When the failure happens after the object was created (any error or cancellation during the data phase), UploadError::partial carries the handle of the possibly-partial object the device still holds, so you can delete it or retry the data phase to resume. The library does not auto-delete it. Cancellation surfaces as Error::Cancelled in UploadError::source. See UploadError for the full contract.

Source

pub async fn create_folder( &self, parent: Option<ObjectHandle>, name: &str, ) -> Result<ObjectHandle, Error>

Source

pub async fn delete(&self, handle: ObjectHandle) -> Result<(), Error>

Source

pub async fn delete_with_cancel( &self, handle: ObjectHandle, cancel: Option<&CancelToken>, ) -> Result<(), Error>

Like delete, but bails with Err(Error::Cancelled) before issuing the PTP DeleteObject request when the token is set.

The PTP DeleteObject is a single fast transaction (no internal loop), so the meaningful cancel point is between recursive iterations on the caller side. This entry point lets callers thread the same token through list and delete without juggling two API shapes.

Source

pub async fn move_object( &self, handle: ObjectHandle, new_parent: ObjectHandle, new_storage: Option<StorageId>, ) -> Result<(), Error>

Move an object to a different folder.

Source

pub async fn copy_object( &self, handle: ObjectHandle, new_parent: ObjectHandle, new_storage: Option<StorageId>, ) -> Result<ObjectHandle, Error>

Source

pub async fn rename( &self, handle: ObjectHandle, new_name: &str, ) -> Result<(), Error>

Rename an object (file or folder).

Not all devices support renaming. Use MtpDevice::supports_rename() to check if this operation is available.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.