Wenn du eine Dokumentverarbeitungspipeline in Rust entwickelst, kennst du den Ablauf: Daten abrufen, Formate konvertieren, saubere Ausgabe erzeugen — bei niedriger Latenz und hohem Durchsatz. Die REST-API von AnyMD passt natürlich zum Rust-Ökosystem, weil beide mit denselben Prioritäten entwickelt wurden: Leistung, Sicherheit und minimaler Overhead.
Dieser Beitrag zeigt, wie du Dokumente aus Rust mit reqwest, tokio und serdein Markdown konvertierst. Benchmarks zeigen, warum die Kombination aus Rust-Client und Rust-Server die niedrigste End-to-End-Latenz liefert. Sieh dir auch unsere Tutorials für Node.js, Python und CLI für andere Sprachen an.
Warum Rust + AnyMD?
Der Konvertierungsserver von AnyMD ist in Rust (Axum + Tokio) geschrieben. Wenn auch dein Client Rust verwendet, entfällt der sprachübergreifende Overhead vollständig — kein Python-GIL, keine Node.js-Event-Loop-Latenz, keine Go-GC-Pausen. Nur direkte asynchrone E/A zwischen zwei stark optimierten Laufzeiten.
Konvertierung eines einzelnen Dokuments
use reqwest::Client;
use std::path::Path;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new();
let api_key = "your-api-key";
let file = Path::new("report.docx");
let file_bytes = tokio::fs::read(file).await?;
let part = reqwest::multipart::Part::bytes(file_bytes)
.file_name("report.docx");
let form = reqwest::multipart::Form::new()
.part("file", part);
let resp = client
.post("https://anymd.net/api/convert")
.header("Authorization", format!("Bearer {api_key}"))
.multipart(form)
.send()
.await?;
let markdown = resp.text().await?;
println!("Got {} chars of Markdown", markdown.len());
Ok(())
}
Stapelkonvertierung mit tokio-Tasks
Wenn du ein Verzeichnis mit Dokumenten hast, startest du parallele Tasks und begrenzt die Nebenläufigkeit mit einem Semaphor.
use reqwest::Client;
use std::sync::Arc;
use std::path::PathBuf;
use tokio::sync::Semaphore;
use tokio::fs;
#[derive(serde::Serialize, serde::Deserialize)]
struct ConversionResult {
file: String,
ok: bool,
chars: usize,
}
async fn convert_one(
client: &Client,
path: &Path,
api_key: &str,
) -> ConversionResult {
let bytes = match fs::read(path).await {
Ok(b) => b,
Err(e) => return ConversionResult {
file: path.to_string_lossy().to_string(),
ok: false,
chars: 0,
},
};
let part = reqwest::multipart::Part::bytes(bytes)
.file_name(path.file_name().unwrap().to_string_lossy().to_string());
let form = reqwest::multipart::Form::new().part("file", part);
match client
.post("https://anymd.net/api/convert")
.header("Authorization", format!("Bearer {api_key}"))
.multipart(form)
.send()
.await
{
Ok(resp) if resp.status().is_success() => {
let text = resp.text().await.unwrap_or_default();
ConversionResult {
file: path.to_string_lossy().to_string(),
ok: true,
chars: text.len(),
}
}
Ok(resp) => ConversionResult {
file: path.to_string_lossy().to_string(),
ok: false,
chars: 0,
},
Err(e) => ConversionResult {
file: path.to_string_lossy().to_string(),
ok: false,
chars: 0,
},
}
}
async fn batch_convert(dir: &str, api_key: &str, concurrency: usize) {
let client = Client::new();
let semaphore = Arc::new(Semaphore::new(concurrency));
let mut entries = Vec::new();
let mut read_dir = tokio::fs::read_dir(dir).await.unwrap();
while let Some(entry) = read_dir.next_entry().await.unwrap() {
entries.push(entry.path());
}
let mut handles = Vec::new();
for path in entries {
let permit = semaphore.clone().acquire_owned().await.unwrap();
handles.push(tokio::spawn(async move {
let result = convert_one(&client, &path, api_key).await;
drop(permit);
result
}));
}
let mut ok = 0usize;
for handle in handles {
let result = handle.await.unwrap();
if result.ok {
ok += 1;
}
println!("{} — {}", result.file, if result.ok { "✓" } else { "✗" });
}
println!("{}/{} converted", ok, entries.len());
}
#[tokio::main]
async fn main() {
batch_convert("./documents", "your-api-key", 10).await;
}
AnyMD für eine RAG-Ingestion-Pipeline in Rust verwenden
Eine realistische Pipeline sieht so aus: ein Verzeichnis auf neue Dateien überwachen, sie in Markdown konvertieren und anschließend für die Vektorsuche chunken und einbetten.
Unterstützte Formate
| Kategorie | Formate |
|---|---|
| Dokumente | PDF, DOCX, ODT, RTF, EPUB, CBR/CBZ |
| Präsentationen | PPTX, ODP |
| Tabellenkalkulationen | XLSX, ODS, CSV (als Tabellen gerendert) |
| Web | HTML, Markdown (unverändert durchgereicht) |
| Bilder | OCR-Extraktion aus JPG, PNG, TIFF |
Preise
| Tarif | Seiten/Monat | Maximale Dateigröße | Preis |
|---|---|---|---|
| Kostenlos | 100 | 10 MB | $0 |
| Starter | 500 | 25 MB | $19 |
| Pro | 5,000 | 50 MB | $99 |
| Enterprise | Individuell | Individuell | Individuell |
Rust-Konvertierungen profitieren von derselben Zero-Retention-Richtlinie und einer Latenz unter einer Sekunde wie alle anderen Sprachen. Sieh in die API-Dokumentation für die vollständige Spezifikation und prüfe die Preise für Tarifdetails.
Hol dir deinen kostenlosen API-Schlüssel — 100 Seiten/Monat, keine Kreditkarte erforderlich.