Skip to main content

nextcloud_uploads/
lib.rs

1//! Nextcloud WebDAV Chunked Upload v2 API client.
2//!
3//! Provides [`UploadClient`] for uploading large files to Nextcloud using the
4//! WebDAV Chunked Upload v2 protocol, and [`UploadSession`] for tracking active
5//! upload progress and calculating chunk ranges.
6//!
7//! # Protocol Overview
8//!
9//! 1. **Initialize Upload Session**:
10//!    - Sends `MKCOL /remote.php/dav/uploads/{user}/{upload_id}`
11//!    - Required header: `Destination: {destination_webdav_url_or_path}`
12//! 2. **Upload Chunks**:
13//!    - Sends `PUT /remote.php/dav/uploads/{user}/{upload_id}/{chunk_number}` (e.g. `00001`, `00002`)
14//!    - Required headers: `Destination: {destination_path}`, `OC-Total-Length: {total_file_size}`
15//!    - Request body: chunk bytes (recommended 5 MiB to 5 GiB, last chunk may be smaller)
16//! 3. **Assemble File**:
17//!    - Sends `MOVE /remote.php/dav/uploads/{user}/{upload_id}/.file`
18//!    - Required headers: `Destination: {destination_path}`, `OC-Total-Length: {total_file_size}`
19//!    - Optional header: `X-OC-Mtime: {unix_timestamp}`
20//! 4. **Abort Upload**:
21//!    - Sends `DELETE /remote.php/dav/uploads/{user}/{upload_id}/` (cancels upload and cleans up temporary chunks)
22//!
23//! # Example
24//!
25//! ```no_run
26//! use nextcloud_uploads::{UploadClient, UploadSession, DEFAULT_CHUNK_SIZE};
27//! # use nextcloud_sdk_core::{Transport, AuthProvider};
28//! # async fn run<T: Transport, A: AuthProvider>(transport: &T, auth: &A) -> Result<(), nextcloud_sdk_core::Error> {
29//! let client = UploadClient::new(transport, auth);
30//!
31//! // 1. Start the upload session
32//! let total_size = 25 * 1024 * 1024; // 25 MiB
33//! let session = client.start("alice", "Documents/archive.zip", total_size).await?;
34//!
35//! // 2. Calculate chunk boundaries and upload each chunk
36//! for boundary in session.chunk_boundaries(DEFAULT_CHUNK_SIZE).unwrap() {
37//!     let chunk_bytes = vec![0u8; boundary.len() as usize];
38//!     client.upload_chunk(&session, boundary.index, chunk_bytes).await?;
39//! }
40//!
41//! // 3. Finish assembly
42//! client.finish(&session, None).await?;
43//! # Ok(())
44//! # }
45//! ```
46
47#![deny(missing_docs)]
48#![forbid(unsafe_code)]
49
50mod client;
51pub mod path;
52mod session;
53
54pub use client::{DESTINATION_HEADER, MTIME_HEADER, TOTAL_LENGTH_HEADER, UploadClient};
55pub use path::{abort_path, assembly_path, chunk_path, upload_collection_path};
56pub use session::{
57    ChunkBoundary, DEFAULT_CHUNK_SIZE, MAX_CHUNK_SIZE, MIN_CHUNK_SIZE, UploadSession,
58};