我的网络开发经验为零。这听起来是一个相当简单的实现方法,但我想我不知道在线搜索时要使用的正确关键字,因为我还没有走得很远。
我想建立一个网站,每当我访问
127.0.0.1:8080/some_number
时,它都会显示 2 个按钮,分别是 Download csv
和 Download txt
。
当我按下按钮时(假设
Download csv
)它应该执行以下操作:
<some_number>.csv
是否已存在于服务器中。touch <some_number>.csv
),然后下载该文件。到目前为止,我已经设法检查文件是否存在:
use axum::extract::Path;
use axum::{routing::get, Router};
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(|| async { "Hello, World!" }))
.route("/:run_number", get(check_files));
let listener = tokio::net::TcpListener::bind("127.0.0.1:8080")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn check_files(Path(run_number): Path<u32>) -> String {
let mut response = String::new();
let csv_file = format!("{run_number}.csv");
let csv_file = std::path::Path::new(&csv_file);
if csv_file.exists() {
response.push_str(&format!("File `{}` does exist\n", csv_file.display()));
} else {
response.push_str(&format!("File `{}` does not exist\n", csv_file.display()));
}
let txt_file = format!("{run_number}.txt");
let txt_file = std::path::Path::new(&txt_file);
if txt_file.exists() {
response.push_str(&format!("File `{}` does exist", txt_file.display()));
} else {
response.push_str(&format!("File `{}` does not exist", txt_file.display()));
}
response
}
有人可以向我指出一些关于如何从这里继续前进的资源吗?
谢谢
这应该可以解决问题。 运行
touch
不是必需的,在 Rust 中可以使用 File::create
创建文件。
/ 端点返回请求文件所需的 html。如果按下按钮,它将从
/{number}/{csv/txt}
端点 请求文件
/index.html
<!DOCTYPE html>
<html>
<body>
<a href="/1/csv" download> Download csv</a>
<a href="/1/txt" download> Download txt</a>
</body>
</html>
/src/main.rs
use axum::{
extract::Path,
http::{header, HeaderMap},
response::Html,
routing::get,
Router,
};
use tokio::{fs, io::AsyncWriteExt};
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/:number/:file_type", get(file_handler))
.route("/", get(index));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn file_handler(
Path((number, filetype)): Path<(i32, String)>,
) -> Result<(HeaderMap, Vec<u8>), String> {
if filetype != "csv" && filetype != "txt" {
return Err("filetype not supported".into());
};
let file_name = format!("{number}.{filetype}");
let Some(file_contents) = read_and_create_file(&file_name).await else {
return Err("Could not read/create file".into());
};
let mut headers = HeaderMap::new();
headers.insert(
header::CONTENT_TYPE,
format!("text/{filetype}; charset=utf-8").parse().unwrap(),
);
headers.insert(
header::CONTENT_DISPOSITION,
format!("attachment; filename=\"{file_name}\"")
.parse()
.unwrap(),
);
Ok((headers, file_contents))
}
async fn read_and_create_file(file_name: &str) -> Option<Vec<u8>> {
let file = std::path::Path::new(file_name);
if file.exists() {
fs::read(file).await.ok()
} else {
let contents: Vec<u8> = b"new file!".into();
let mut file = fs::File::create(file).await.ok()?;
file.write_all(&contents).await.ok()?;
file.flush().await.ok()?;
Some(contents)
}
}
async fn index() -> Html<Vec<u8>> {
let html = fs::read("index.html").await.unwrap();
Html(html)
}