Skip to content

Add directory upload helper for recursive uploads #46

@joshrotenberg

Description

@joshrotenberg

Problem

Ruby SDK issue #18: User asked "Upload a whole directory that contain images" - no clear solution provided. This is a common need for bulk operations.

Proposed Solution

Add recursive directory upload helper:

use std::path::Path;
use walkdir::WalkDir;

impl FileHandler {
    /// Uploads an entire directory recursively
    pub async fn upload_directory(
        &self,
        local_dir: &Path,
        remote_path: &str,
        mkdir_parents: bool,
    ) -> Result<Vec<String>> {
        let mut uploaded = Vec::new();
        
        for entry in WalkDir::new(local_dir) {
            let entry = entry?;
            
            if entry.file_type().is_file() {
                let local_file = entry.path();
                
                // Calculate relative path
                let relative = local_file
                    .strip_prefix(local_dir)?
                    .to_str()
                    .ok_or_else(|| FilesError::InvalidPath {
                        path: local_file.display().to_string(),
                        reason: "Invalid UTF-8 in path".to_string(),
                    })?;
                
                // Construct remote path
                let remote_file = format!("{}/{}", remote_path.trim_end_matches('/'), relative);
                
                // Read and upload
                let data = std::fs::read(local_file)?;
                self.upload_file_with_options(&remote_file, &data, mkdir_parents).await?;
                
                uploaded.push(remote_file);
            }
        }
        
        Ok(uploaded)
    }
    
    /// Upload directory with progress callback
    pub async fn upload_directory_with_progress<F>(
        &self,
        local_dir: &Path,
        remote_path: &str,
        mkdir_parents: bool,
        progress: F,
    ) -> Result<Vec<String>>
    where
        F: Fn(usize, usize) // (current, total)
    {
        // Count files first
        let total_files = WalkDir::new(local_dir)
            .into_iter()
            .filter_map(|e| e.ok())
            .filter(|e| e.file_type().is_file())
            .count();
        
        let mut uploaded = Vec::new();
        
        for (idx, entry) in WalkDir::new(local_dir)
            .into_iter()
            .filter_map(|e| e.ok())
            .filter(|e| e.file_type().is_file())
            .enumerate()
        {
            // Upload logic (same as above)
            // ...
            
            progress(idx + 1, total_files);
        }
        
        Ok(uploaded)
    }
}

Usage Example

use std::path::Path;

// Simple upload
let uploaded = handler.upload_directory(
    Path::new("./local/images"),
    "/remote/uploads",
    true  // mkdir_parents
).await?;

println!("Uploaded {} files: {:?}", uploaded.len(), uploaded);

// With progress
handler.upload_directory_with_progress(
    Path::new("./data"),
    "/backups",
    true,
    |current, total| {
        println!("Progress: {}/{}", current, total);
    }
).await?;

Dependencies

Add to Cargo.toml:

walkdir = "2.5"

Testing

Add integration test:

#[tokio::test]
async fn test_upload_directory() {
    // Create temp directory with test files
    // Upload to Files.com
    // Verify all files uploaded
    // Cleanup
}

Documentation

  • Add example to README
  • Document error handling (partial uploads)
  • Show progress callback usage
  • Note performance considerations

Future Enhancements

  • Filter patterns (ignore .git, etc.)
  • Parallel uploads (use tokio::spawn)
  • Resume capability for failed uploads

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions