文件上传-通过表单上传
1 前端:
我们先看表单上传文件的请求协议:
补充: 下文 [Content of aaa.txt] 代表文件数据.
POST /upload HTTP/1.1 Host: example.com Content-Type: multipart/form-data; boundary=boundary123 --boundary123 Content-Disposition: form-data; name="file"; filename="aaa.txt" Content-Type: text/plain [Content of aaa.txt] --boundary123 Content-Disposition: form-data; name="file"; filename="bbb.tar" Content-Type: application/x-tar [Content of bbb.tar] --boundary123 Content-Disposition: form-data; name="file"; filename="ccc.zip" Content-Type: application/zip [Content of ccc.zip] --boundary123--
通过协议可以看到文件上传的核心是下面三部分:
请求方式: POST
内容类型: Content-Type: multipart/form-data; boundary=boundary123
具体文件数据: [Content of 文件数据]
我们需要在代码中给全这三部分.
前端通过表单上传文件:
angular:
html:
<form (submit)="upload(myUploadFiles.files)"> <input type="file" name="file" #myUploadFiles multiple> <button type="submit">上传文件</button> </form>
ts:
upload(fileList: FileList | null) { if (fileList) { const formData = new FormData(); for (let i = 0; i < fileList.length; i++) { formData.append(files[i].name, fileList[i]);
} this.http.post('/api/upload/', formData).subscribe( (res) => { console.log('上传成功', res); }, (error) => { console.error('上传失败', error); }, ); } }
2 后端 :
后端采用 rust actix_web 框架:
Cargo.toml
actix-web = { version = "4", features = ["openssl"] } openssl = { version = "0.10", features = ["v110"] } tokio = { version = "1.38.0", features = ["full"] } async-stream = "0.3" futures-util = "0.3.30" actix-multipart = "0.4"
rust
use actix_web::{ http::header::{ContentDisposition, ContentType, DispositionParam, DispositionType, HeaderValue}, get, post, web, Error, App, HttpRequest, HttpResponse, HttpServer, Responder, }; use std::path::Path; use tokio::io::AsyncReadExt; use openssl::ssl::{SslFiletype, SslAcceptor, SslMethod}; use actix_multipart::Multipart; use tokio::io::AsyncWriteExt; use futures_util::StreamExt; use tokio::fs;
#[actix_web::main] async fn main() -> std::io::Result<()> { let addr = "127.0.0.1:8888"; println!("准备监听: {}", addr); let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); builder .set_private_key_file( "D:\\server.key", SslFiletype::PEM, ) .unwrap(); builder .set_certificate_chain_file("D:\\server.crt") .unwrap(); HttpServer::new(|| { App::new() .service(my_download) .service(my_upload) }) .bind_openssl(addr, builder)? .run() .await }
#[post("/api/upload")]
async fn my_upload(mut payload: Multipart) -> Result<HttpResponse, Error> {
println!("文件上传被触发");
while let Some(item) = payload.next().await {
let mut field = item?;
// Generate a unique file name
// let filename = format!("upload_{}", "my_test");
let filename = field.content_disposition().get_filename().unwrap();
println!("{}", filename);
// File path where the file will be saved
let filepath = format!("F:/{}", filename);
// Create file
let mut f = fs::File::create(filepath).await?;
// Write file contents
while let Some(chunk) = field.next().await {
let data = chunk?;
f.write_all(&data).await?;
}
}
Ok(HttpResponse::Ok().body("{\"upload\": \"File uploaded success\"}"))
}