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

如何在Axum代理服务中实现文件直接下载至浏览器?

解决Axum代理返回CSV不触发浏览器下载的问题

要让浏览器直接下载CSV文件,需要修改三个核心部分:返回类型、内容获取方式以及响应头设置,具体如下:

修改要点

  • 替换返回类型:放弃Json<String>,改用支持自定义响应头的impl IntoResponse,这样能完全控制响应的格式和行为。
  • 获取原始字节内容:用response.bytes().await?替代response.text().await?,避免字符编码转换导致CSV格式错乱。
  • 添加下载触发头:
    • 设置Content-Type为text/csv; charset=utf-8,明确告知浏览器这是CSV文件。
    • 设置Content-Disposition为attachment; filename="xxx.csv",强制浏览器触发下载动作并指定保存的文件名。

修改后的完整代码

use std::net::SocketAddr;
use axum::{
    Router, Server,
    response::{IntoResponse, Response},
    http::{HeaderMap, header::{CONTENT_TYPE, CONTENT_DISPOSITION}}
};
use axum::routing::get;

// 自定义错误类型,实现IntoResponse以便Axum处理错误响应
#[derive(Debug)]
struct ApiError(reqwest::Error);

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        (axum::http::StatusCode::INTERNAL_SERVER_ERROR, self.0.to_string()).into_response()
    }
}

impl From<reqwest::Error> for ApiError {
    fn from(err: reqwest::Error) -> Self {
        ApiError(err)
    }
}

pub async fn downloadfile() -> Result<impl IntoResponse, ApiError> {
    let filename = "target_file.csv"; // 替换为实际的文件名
    let endpoint = "http://127.0.0.1:6101/api/CSV/DownloadFile";
    let path = format!("{}?filename={}", endpoint, filename);

    let response = reqwest::get(path).await?;
    // 获取原始字节流,保留CSV的原始格式和编码
    let content_bytes = response.bytes().await?;

    // 构建响应头
    let mut headers = HeaderMap::new();
    headers.insert(
        CONTENT_TYPE,
        "text/csv; charset=utf-8".parse().unwrap()
    );
    headers.insert(
        CONTENT_DISPOSITION,
        format!("attachment; filename=\"{}\"", filename).parse().unwrap()
    );

    // 返回(响应头, 文件字节),Axum会自动组装成符合要求的HTTP响应
    Ok((headers, content_bytes))
}

pub async fn serve(listen_addr: SocketAddr) {
    let app = Router::new()
        .route("/downloadfile", get(downloadfile));

    Server::bind(&listen_addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

关键说明

  • 移除Json包装后,响应体不再是JSON格式,而是直接返回CSV的原始字节,配合正确的响应头,浏览器会识别为可下载文件。
  • 使用bytes()而非text()可以避免因字符编码(如UTF-8以外的编码)导致的CSV内容乱码问题。
  • 如果你的项目已有成熟的错误处理逻辑,可以替换示例中的ApiError实现,只需保证它能转换为Axum的Response即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 04:54:16