|
| 1 | +/// Example how to request a blob from a remote node without using a store. |
| 2 | +mod common; |
| 3 | +use bao_tree::io::BaoContentItem; |
| 4 | +use clap::Parser; |
| 5 | +use common::setup_logging; |
| 6 | +use iroh::discovery::pkarr::PkarrResolver; |
| 7 | +use iroh_blobs::{get::request::GetBlobItem, ticket::BlobTicket, BlobFormat}; |
| 8 | +use n0_future::StreamExt; |
| 9 | +use tokio::io::AsyncWriteExt; |
| 10 | + |
| 11 | +#[derive(Debug, Parser)] |
| 12 | +#[command(version, about)] |
| 13 | +pub struct Cli { |
| 14 | + /// Ticket describing the content to fetch and the node to fetch it from |
| 15 | + /// |
| 16 | + /// This example only supports raw blobs. |
| 17 | + ticket: BlobTicket, |
| 18 | + /// True to print data as it arrives, false to complete the download and then |
| 19 | + /// print the data. Defaults to true. |
| 20 | + /// |
| 21 | + /// Note that setting progress to false can lead to an out-of-memory error |
| 22 | + /// for very large blobs. |
| 23 | + #[arg(long, default_value = "true")] |
| 24 | + progress: bool, |
| 25 | +} |
| 26 | + |
| 27 | +#[tokio::main] |
| 28 | +async fn main() -> anyhow::Result<()> { |
| 29 | + setup_logging(); |
| 30 | + let cli = Cli::parse(); |
| 31 | + let ticket = cli.ticket; |
| 32 | + let endpoint = iroh::Endpoint::builder() |
| 33 | + .discovery(PkarrResolver::n0_dns()) |
| 34 | + .bind() |
| 35 | + .await?; |
| 36 | + anyhow::ensure!( |
| 37 | + ticket.format() == BlobFormat::Raw, |
| 38 | + "This example only supports raw blobs." |
| 39 | + ); |
| 40 | + let connection = endpoint |
| 41 | + .connect(ticket.node_addr().node_id, iroh_blobs::ALPN) |
| 42 | + .await?; |
| 43 | + let mut progress = iroh_blobs::get::request::get_blob(connection, ticket.hash()); |
| 44 | + let stats = if cli.progress { |
| 45 | + loop { |
| 46 | + match progress.next().await { |
| 47 | + Some(GetBlobItem::Item(item)) => match item { |
| 48 | + BaoContentItem::Leaf(leaf) => { |
| 49 | + tokio::io::stdout().write_all(&leaf.data).await?; |
| 50 | + } |
| 51 | + BaoContentItem::Parent(parent) => { |
| 52 | + tracing::info!("Parent: {parent:?}"); |
| 53 | + } |
| 54 | + }, |
| 55 | + Some(GetBlobItem::Done(stats)) => { |
| 56 | + break stats; |
| 57 | + } |
| 58 | + Some(GetBlobItem::Error(err)) => { |
| 59 | + anyhow::bail!("Error while streaming blob: {err}"); |
| 60 | + } |
| 61 | + None => { |
| 62 | + anyhow::bail!("Stream ended unexpectedly."); |
| 63 | + } |
| 64 | + } |
| 65 | + } |
| 66 | + } else { |
| 67 | + let (bytes, stats) = progress.bytes_and_stats().await?; |
| 68 | + tokio::io::stdout().write_all(&bytes).await?; |
| 69 | + stats |
| 70 | + }; |
| 71 | + tracing::info!("Stream done with stats: {stats:?}"); |
| 72 | + Ok(()) |
| 73 | +} |
0 commit comments