You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust(tokio+axum)中将genpdf生成的PDF返回给客户端?

解决genpdf生成PDF流式返回给Axum客户端的问题

原代码存在的问题

  • Reade是拼写错误,应为ReaderStream
  • doc.render(w)中的w未定义,genpdf的render需要一个实现std::io::Write的对象
  • HeaderMap创建后未声明为可变,直接insert会报错
  • 函数返回类型与内部Ok分支不匹配,缺少错误处理的返回逻辑

修正后的完整代码

use axum::{
    http::{header, HeaderMap, HeaderValue},
    response::{IntoResponse, StreamBody},
};
use genpdf::{elements::Text, fonts::from_files, Document, SimplePageDecorator};
use std::io::Cursor;
use tokio_util::io::ReaderStream;

async fn make_pdf() -> Result<impl IntoResponse, Box<dyn std::error::Error>> {
    // 加载字体文件
    let font_family = from_files("./fonts", "Calibri", None)
        .expect("Failed to load font");
    
    // 构建PDF文档结构
    let mut doc = Document::new(font_family);
    doc.set_title("Test Title");
    
    let mut decorator = SimplePageDecorator::new();
    decorator.set_margins(10);
    doc.set_page_decorator(decorator);
    
    doc.push(Text::new("First PDF Text"));
    
    // 将PDF渲染到内存缓冲区
    let mut pdf_data = Vec::new();
    doc.render(&mut pdf_data)?;
    
    // 将内存数据转换为异步可读流
    let cursor = Cursor::new(pdf_data);
    let stream = ReaderStream::new(cursor);
    
    // 设置响应头,指定文件类型和下载属性
    let mut headers = HeaderMap::new();
    headers.insert(
        header::CONTENT_TYPE,
        HeaderValue::from_static("application/pdf"),
    );
    headers.insert(
        header::CONTENT_DISPOSITION,
        HeaderValue::from_static("attachment; filename=\"Cargo.pdf\""),
    );
    
    // 返回带响应头的流式响应
    Ok((headers, StreamBody::new(stream)))
}

关键修改说明

  • 用Vec<u8>作为内存缓冲区接收PDF渲染结果,避免额外文件IO开销
  • 通过Cursor<Vec<u8>>将静态内存数据转换为AsyncRead实现,适配ReaderStream的异步流要求
  • 声明mut headers以支持插入响应头字段
  • 修正函数返回类型为Result,统一处理渲染和类型转换中的错误
  • 使用HeaderValue::from_static简化响应头创建,避免字符串解析错误

内容的提问来源于stack exchange,提问作者Luck Proit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 23:01:17