impliment file upload backend and frontend with rust
impliment with actix_web
use std::path::PathBuf; use actix_web::{web, App, HttpResponse, HttpServer}; use futures::StreamExt; #[allow(warnings)] async fn upload(mut payload: web::Payload) -> HttpResponse { let path = "./stream.data"; let pathbuf = PathBuf::from(path); if pathbuf.exists(){ std::fs::remove_file(pathbuf.clone()).unwrap(); } while let Some(item) = payload.next().await { let chunk = item.unwrap(); doe::fs::append_data_to_file(path, chunk.to_vec()).unwrap(); } if let Ok(d) = std::fs::read(path){ if let Some(ex) = get_file_extension(&d){ std::fs::copy(path, format!("./stream.{}",ex)).unwrap(); }else{ std::fs::copy(path, format!("./stream")).unwrap(); } } if pathbuf.exists(){ std::fs::remove_file(pathbuf).unwrap(); } HttpResponse::Ok().finish() } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .service(web::resource("/").route(web::post().to(upload))) }) .bind("127.0.0.1:8080")? .run() .await }
impliment with rocket
lib.rs
#![allow(unused)] fn main() { pub mod file_upload_server{ use log::info; use rocket::{Data, post, routes, Rocket, get, response::content::Json}; use rocket_contrib::serve::StaticFiles; use std::{fs, path::PathBuf, net::{IpAddr, SocketAddr}}; #[allow(warnings)] #[post("/", data = "<data>")] fn upload(data: Data) -> Result<Json<&'static str>, std::io::Error> { let path = "./stream.data"; let pathbuf = PathBuf::from(path); if pathbuf.exists() { fs::remove_file(pathbuf.clone()).unwrap(); } let mut file = fs::File::create(path)?; data.stream_to_file(path) .map(|n| format!("Wrote {} bytes to stream.data", n)).unwrap(); if let Ok(buf) = fs::read(path) { if !buf.starts_with(&[0x2D,0x2D,0x2D,0x2D,0x2D,0x2D]){ if let Some(ex) = get_file_extension(&buf) { fs::copy(path, format!("./stream.{}", ex)).unwrap(); } else { fs::copy(path, format!("./stream")).unwrap(); } }else{ use doe::*; let mut buf = buf.clone(); let remove_last = buf.split_at(buf.len()-46).0.to_vec(); let remove_first_contet = remove_last.split_at(97).1; let (head,other) = remove_first_contet.split_at(200); let s = vec_element_to_string!(head).join(":"); let file_name = vec_element_clone!(split_to_vec!(String::from_utf8_lossy(head),"\""),0); let s = split_to_vec!(s,":13:10:13:10:").last().unwrap().to_string(); let mut s_vec = split_to_vec!(s,":").into_iter().filter(|s|!s.is_empty()).map(|s|s.trim().to_string().parse::<u8>().unwrap()).collect::<Vec<u8>>(); s_vec.extend(other.iter()); std::fs::write(file_name, s_vec).unwrap(); } } if pathbuf.exists() { fs::remove_file(pathbuf).unwrap(); } Ok(Json("{\"status\":\"Ok\"}")) } #[allow(warnings)] use rocket::response::{content, Content}; use rocket::http::ContentType; #[get("/")] fn index() -> Content<&'static str> { let content_type = ContentType::HTML; let body = include_str!("./html/index.html"); Content(content_type, body) } #[allow(warnings)] fn get_file_extension(data: &[u8]) -> Option<&'static str> { let ext = match data[..] { // Windows PE 可执行文件 [b'M', b'Z', ..] => Some("exe"), // Linux ELF 可执行文件 [0x7f, b'E', b'L', b'F', ..] => Some("elf"), // PNG 图像文件 [b'\x89', b'P', b'N', b'G', ..] => Some("png"), // HTML 文档 [b'<', b'!', b'D', b'O', b'C', b'T', ..] => Some("html"), // PDF 文件 [b'%', b'P', b'D', b'F', ..] => Some("pdf"), // ZIP 压缩文件 [b'P', b'K', 3, 4, ..] => Some("zip"), // 7z 压缩文件 [0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c, ..] => Some("7z"), // RAR 压缩文件 [b'R', b'a', b'r', b'!', ..] => Some("rar"), // GZIP 压缩文件 [b'\x1f', b'\x8b', 8, 0, ..] => Some("gz"), // BZIP2 压缩文件 [b'B', b'Z', b'h', ..] => Some("bz2"), // CPIO 归档文件 [b'I', b's', b'c', b'(', ..] => Some("cpio"), // TAR 归档文件 [b'u', b's', b't', b'a', b'r', ..] => Some("tar"), // CHM 帮助文件 [b'I', b'T', b'S', b'F', ..] => Some("chm"), // MP3 音频文件 [b'I', b'D', b'3', ..] => Some("mp3"), // AVI 视频文件 [b'R', b'I', b'F', b'F', ..] => Some("avi"), // JPEG/JFIF 图像文件 [b'\xff', b'\xd8', b'\xff', ..] => Some("jpg"), // BMP 图像文件 [b'B', b'M', ..] => Some("bmp"), // ICO 图标文件 [0, 1, ..] => Some("ico"), // WAV 音频文件 [b'R', b'I', b'F', b'F', ..] => Some("wav"), // Ogg 容器 [b'O', b'g', b'g', b'S', ..] => Some("ogg"), // FLAC 音频文件 [b'f', b'L', b'a', b'C', ..] => Some("flac"), // Impress 演示文稿 [b'I', b'M', b'P', b'S', ..] => Some("impress"), // MIDI 文件 [b'M', b'T', b'h', b'd', ..] => Some("midi"), // TIFF 图像文件 [b'M', b'M', 0, 42, ..] => Some("tiff"), // CR2 图像文件 [b'I', b'I', 0x2a, 0, 0x10, 0, 0, 0, b'C', b'R', ..] => Some("cr2"), // NEF 图像文件 [b'M', b'M', 0, 0x2a, ..] => Some("nef"), // WebP 图像文件 [b'W', b'E', b'B', b'P', ..] => Some("webp"), // Cab 归档文件 [b'M', b'S', b'C', b'F', ..] => Some("cab"), // Microsoft Office Open XML 文档 [b'P', b'K', 3, 4, ..] => Some("xlsx"), // XZ 压缩格式 [b'\xfd', b'7', b'z', b'X', b'Z', 0, ..] => Some("xz"), // iCalendar 文档 [b'B', b'E', b'G', b'I', b'N', b':', b'V', b'C', ..] => Some("ics"), // vCard 文档 [b'B', b'E', b'G', b'I', b'N', b':', b'V', b'C', b'A', b'R', b'D', ..] => Some("vcf"), // SQLite 数据库文件 [b'S', b'Q', b'L', b'i', b't', b'e', b' ', b'f', b'o', b'r', b'm', b'a', b't', b' ', b'3', ..] => Some("sqlite"), // GIF 图像文件 [b'G', b'I', b'F', b'8', b'7', b'a', ..] | [b'G', b'I', b'F', b'8', b'9', b'a', ..] => Some("gif"), _ => None, }; ext } pub async fn start() { let ip_addr: IpAddr = match local_ipaddress::get() { Some(x)=>{ x.parse().expect("Found local IP Address is invalid") }, None=>IpAddr::from([127, 0, 0, 1]) }; use rocket::config::{Config, Environment}; let socket_addr = SocketAddr::from((ip_addr, 8585)); info!("Server listening on http://{}", socket_addr); let config = Config::build(Environment::Production) .address(socket_addr.ip().to_string()) // 设置应用程序监听地址 .port(socket_addr.port()) // 设置应用程序监听端口 .finalize() .unwrap(); Rocket::custom(config) .mount("/", StaticFiles::from("static")) .mount("/", routes![upload,index]) .launch(); } } }
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Upload</title>
</head>
<style>
h1 {
text-align: center;
}
form {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 50px;
}
input[type=file] {
margin-bottom: 20px;
font-size: 16px;
padding: 10px;
border-radius: 5px;
background-color: #f2f2f2;
border: none;
}
button[type=submit] {
padding: 10px 20px;
background-color: #4CAF50;
color: #fff;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
}
#message {
display: none; /* 初始时隐藏 */
width: 100%;
padding: 20px;
background-color: #4CAF50;
color: #fff;
text-align: center;
margin-top: 50px;
}
</style>
<body>
<h1>File Upload</h1>
<form method="post" action="/" enctype="application/x-www-form-urlencoded">
<input type="file" name="file">
<button type="submit">Upload</button>
</form>
<div id="message"></div> <!-- 显示上传成功信息的 div -->
</body>
<script>
const form = document.querySelector('form');
const message = document.querySelector('#message');
form.addEventListener('submit', (event) => {
event.preventDefault();
const fileInput = document.querySelector('input[type=file]');
const files = fileInput.files;
if (files.length === 0) {
alert('Please select a file!');
return;
}
const formData = new FormData();
formData.append('file', files[0]);
fetch('/', { method: 'POST', body: formData })
.then(response => response.json())
.then(data => {
if (data.status) {
showSuccessMessage();
} else {
alert(data.message);
}
});
});
function showSuccessMessage() {
message.innerHTML = 'File uploaded successfully!';
message.style.display = 'block';
setTimeout(() => {
message.style.display = 'none';
}, 3000); // 3秒后自动隐藏提示信息
}
</script>
</html>