rust 怎么实现 sftp 文件的 web 流式下载

2021-06-23 16:46:26 +08:00
 Jrohy

web 框架用的是 actix-web, sftp 访问用的是 ssh2, 以下测试代码, 调接口后 sftp 的文件加载到内存完成后才能开始下载。
想实现 sftp 的文件不用加载到内存和下载到本地服务器,调用 web 接口可以马上下载的效果

#[get("/")]
async fn file_download() -> HttpResponse{

    let tcp = TcpStream::connect("1.2.3.4:22").unwrap();
    let mut sess = Session::new().unwrap();
    sess.set_tcp_stream(tcp);
    sess.handshake().unwrap();
    sess.userauth_password("root", "abcde").unwrap();

    let file_path = "/root/gg.zip";

    let (mut remote_file, _stat) = sess.scp_recv(Path::new(file_path)).unwrap();

    let (tx, rx) = local_channel::mpsc::channel::<Result<Bytes, Error>>();
    actix_web::rt::spawn(async move {
        let mut buffer = [0; 100];
        loop {
            if !remote_file.eof() {
                if let Ok(_n) = remote_file.read(&mut buffer[..]) {
                    let data = Vec::from(buffer);
                    tx.send(Ok(Bytes::from(data))).unwrap();
                } else {
                    break
                }
            } else {
                break
            }
        }
    });
    HttpResponse::Ok()
        .set_header(actix_web::http::header::CONTENT_DISPOSITION, format!("attachment; filename={}", file_path.split("/").last().unwrap()))
        .streaming(rx)
}

如果换做用 go, 用 io.copy 就能实现

c.Writer.WriteHeader( http.StatusOK)
c.Header("Content-Disposition", "attachment; filename=gg.txt")
_, _ = io.Copy(c.Writer, sftpFile)

rust 这方面资料实在太少(连搜索英文都搜不到什么),rust 也有 io::copy 但不知道怎么绑定到 httpresponse, 希望有经验人士能解答下,即使换 web 框架都可以(不用 actix-web)

2297 次点击
所在节点    Rust
4 条回复
12101111
2021-06-23 17:45:42 +08:00
建议你使用异步的 io::copy https://docs.rs/async-std/1.9.0/async_std/io/fn.copy.html

然后 ssh2 也用异步的 https://docs.rs/async-ssh2-lite/0.2.1/async_ssh2_lite/struct.AsyncFile.html

上面两个用的似乎是 async-std/smol 的库, 不过 actix-web 用的是 tokio, 不知道是不是兼容, tokio 有自己的 io::copy https://docs.rs/tokio/1.7.1/tokio/io/fn.copy.html
Jrohy
2021-06-23 18:02:53 +08:00
@12101111 感谢回复,如果能有例子最好了(rust 新手摸索中),我先看下
araaaa
2021-06-24 11:08:18 +08:00
用的是标准库的 stream 就用 std::io::copy,tokio 的就调 tokio::io::copy
Jrohy
2021-06-24 14:08:28 +08:00
我试了下 rocket.rs 框架的 bytestream 可以实现, 但没法指定下载流生成的文件名😭
```
ByteStream! {
let mut buffer = [0; 1000];
loop {
if !scp_file.eof() {
if let Ok(_n) = scp_file.read(&mut buffer[..]) {
let data = Vec::from(buffer);
yield data;
} else {
break
}
} else {
break
}
}
}
```

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/785352

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX