Skip to content

Commit 8412beb

Browse files
committed
上传rust程序部分
1 parent 0c908bc commit 8412beb

File tree

3 files changed

+369
-1
lines changed

3 files changed

+369
-1
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
config.json
22
Recovery_Backup_Core.exe
3-
Recovery_Backup_Core.pdb
3+
Recovery_Backup_Core.pdb
4+
/Recovery_Backup_Core/.idea
5+
/Recovery_Backup_Core/target
6+
/Recovery_Backup_Core/Cargo.lock
7+
/Recovery_Backup_Core/.gitignore

Recovery_Backup_Core/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "Recovery_Backup_Core"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
zip= "2.1.6"
8+
rayon = "1.10.0"
9+
reqwest = { version = "0.12.5", features = ["stream"] }
10+
tokio = { version = "1.0.0", features = ["rt", "rt-multi-thread", "macros"] }
11+
futures = "0.3.30"
12+
tokio-util = "0.7.11"

Recovery_Backup_Core/src/main.rs

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
use std::env;
2+
use std::fs;
3+
use std::io;
4+
use std::thread;
5+
use std::fs::File;
6+
use std::io::{Read, Write};
7+
use std::thread::sleep;
8+
use std::sync::{Arc, Mutex};
9+
use std::path::{Path, PathBuf};
10+
use std::process::{Command, Stdio};
11+
use std::time::{Duration, SystemTime, UNIX_EPOCH};
12+
use reqwest::Client;
13+
use zip::ZipArchive;
14+
use rayon::prelude::*;
15+
use futures::future::BoxFuture;
16+
use futures::FutureExt;
17+
18+
async fn upload_file(client: &Client, file_path: &Path, url: &str, username: &str, password: &str) -> Result<(), Box<dyn std::error::Error>> {
19+
let file = File::open(file_path)?;
20+
let file_stream = reqwest::Body::wrap_stream(tokio_util::codec::FramedRead::new(tokio::fs::File::from_std(file), tokio_util::codec::BytesCodec::new()));
21+
22+
let res = client
23+
.put(url)
24+
.basic_auth(username, Some(password))
25+
.body(file_stream)
26+
.send()
27+
.await?;
28+
29+
if res.status().is_success() {
30+
println!("文件上传成功: {}", file_path.display());
31+
} else {
32+
eprintln!("文件上传失败: {}", res.status());
33+
}
34+
35+
Ok(())
36+
}
37+
38+
fn upload_directory<'a>(client: &'a Client, dir_path: &'a Path, base_url: &'a str, remote_path: &'a str, username: &'a str, password: &'a str) -> BoxFuture<'a, Result<(), Box<dyn std::error::Error>>> {
39+
async move {
40+
let entries = fs::read_dir(dir_path)?;
41+
42+
for entry in entries {
43+
let entry = entry?;
44+
let path = entry.path();
45+
let relative_path = path.strip_prefix(dir_path)?;
46+
let remote_path_str = remote_path.strip_prefix('/').unwrap_or(remote_path);
47+
let remote_file_url = if path.is_file() {
48+
format!("{}/{}/{}", base_url.strip_suffix('/').unwrap_or(base_url), remote_path_str, relative_path.display())
49+
} else {
50+
format!("{}/{}/{}", base_url.strip_suffix('/').unwrap_or(base_url), remote_path_str, relative_path.display())
51+
};
52+
53+
if path.is_file() {
54+
println!("上传文件: {}", remote_file_url); // 调试信息
55+
upload_file(client, &path, &remote_file_url, username, password).await?;
56+
} else if path.is_dir() {
57+
// 创建远程目录
58+
let res = client
59+
.request(reqwest::Method::from_bytes(b"MKCOL")?, &remote_file_url)
60+
.basic_auth(username, Some(password))
61+
.send()
62+
.await?;
63+
64+
if res.status().is_success() {
65+
println!("目录创建成功: {}", remote_file_url);
66+
} else {
67+
eprintln!("目录创建失败: {}", res.status());
68+
}
69+
70+
// 递归上传子目录内容
71+
upload_directory(client, &path, base_url, &remote_file_url.strip_prefix(base_url).unwrap_or(&remote_file_url), username, password).await?;
72+
}
73+
}
74+
75+
Ok(())
76+
}.boxed()
77+
}
78+
79+
async fn upload_backup(file_path: &Path, webdav_url: &str, remote_path: &str, username: &str, password: &str) -> Result<(), Box<dyn std::error::Error>> {
80+
let client = Client::new();
81+
82+
if file_path.is_file() {
83+
// 如果是文件,上传文件
84+
let file_name = file_path.file_name().unwrap().to_str().unwrap();
85+
let remote_file_url = format!("{}/{}", webdav_url.strip_suffix('/').unwrap_or(webdav_url), remote_path.strip_prefix('/').unwrap_or(remote_path).to_string() + "/" + file_name);
86+
println!("准备上传文件到: {}", remote_file_url); // 调试信息
87+
upload_file(&client, file_path, &remote_file_url, username, password).await
88+
} else if file_path.is_dir() {
89+
// 如果是目录,上传目录内容
90+
println!("准备上传目录: {}", file_path.display()); // 调试信息
91+
upload_directory(&client, file_path, webdav_url, remote_path, username, password).await
92+
} else {
93+
Err("提供的路径无效".into())
94+
}
95+
}
96+
97+
98+
99+
100+
101+
fn copy_dir_recursive(src: &Path, dst: &Path) -> io::Result<()> {
102+
if !dst.exists() {
103+
fs::create_dir(dst)?;
104+
}
105+
106+
let entries: Vec<_> = fs::read_dir(src)?.collect();
107+
108+
entries.par_iter().for_each(|entry| {
109+
let entry = entry.as_ref().unwrap();
110+
let path = entry.path();
111+
let dst_path = dst.join(entry.file_name());
112+
113+
if path.is_dir() {
114+
copy_dir_recursive(&path, &dst_path).unwrap();
115+
} else {
116+
fs::copy(&path, &dst_path).unwrap();
117+
}
118+
});
119+
120+
Ok(())
121+
}
122+
123+
fn delete_old_backups(backup_path: &Path, max_age_days: u64) -> io::Result<()> {
124+
let now = SystemTime::now();
125+
let cutoff_time = now - std::time::Duration::from_secs(max_age_days * 24 * 60 * 60);
126+
127+
let mut files = Vec::new();
128+
for entry in fs::read_dir(backup_path)? {
129+
let entry = entry?;
130+
let path = entry.path();
131+
132+
if path.is_file() {
133+
let metadata = fs::metadata(&path)?;
134+
let modified_time = metadata.modified()?;
135+
if modified_time < cutoff_time {
136+
files.push(path);
137+
}
138+
}
139+
}
140+
141+
let files = Arc::new(Mutex::new(files));
142+
let mut handles = vec![];
143+
const NUM_THREADS: usize = 4;
144+
145+
for _ in 0..NUM_THREADS {
146+
let files = Arc::clone(&files);
147+
let handle = thread::spawn(move || {
148+
while let Some(path) = files.lock().unwrap().pop() {
149+
if let Err(e) = fs::remove_file(&path) {
150+
eprintln!("Failed to delete file: {:?}", e);
151+
} else {
152+
println!("Deleted old backup file: {:?}", path);
153+
}
154+
}
155+
});
156+
handles.push(handle);
157+
}
158+
159+
for handle in handles {
160+
handle.join().unwrap();
161+
}
162+
163+
Ok(())
164+
}
165+
166+
const BUFFER_SIZE: usize = 8 * 1024; // 8 KB
167+
168+
fn unzip_backup(zip_path: &Path, target_dir: &Path) -> io::Result<()> {
169+
let file = File::open(zip_path)?;
170+
let archive = Arc::new(Mutex::new(ZipArchive::new(file)?));
171+
let file_count = archive.lock().unwrap().len();
172+
173+
(0..file_count).into_par_iter().try_for_each(|i| {
174+
let archive = Arc::clone(&archive);
175+
let mut archive = archive.lock().unwrap();
176+
let mut file = archive.by_index(i)?;
177+
178+
let outpath = target_dir.join(file.name());
179+
180+
if (*file.name()).ends_with('/') {
181+
fs::create_dir_all(&outpath)?;
182+
} else {
183+
if let Some(p) = outpath.parent() {
184+
if !p.exists() {
185+
fs::create_dir_all(p)?;
186+
}
187+
}
188+
let mut outfile = File::create(&outpath)?;
189+
190+
let mut buffer = vec![0; BUFFER_SIZE];
191+
loop {
192+
let bytes_read = file.read(&mut buffer)?;
193+
if bytes_read == 0 {
194+
break;
195+
}
196+
outfile.write_all(&buffer[..bytes_read])?;
197+
}
198+
}
199+
200+
Ok::<(), io::Error>(())
201+
})?;
202+
203+
Ok(())
204+
}
205+
206+
fn recover_backup(backup_path: &Path, target_dir: &Path, world_name: &str, server_exe: &str) -> io::Result<()> {
207+
let worlds_dir = target_dir.join("worlds");
208+
let world_path = worlds_dir.join(world_name);
209+
let server_exe_path = target_dir.join(server_exe);
210+
for _ in 0..3 {
211+
// 查找并终止服务器进程
212+
if let Err(e) = terminate_server_process(&server_exe_path) {
213+
eprintln!("Error terminating server process: {}", e);
214+
} else {
215+
break;
216+
}
217+
thread::sleep(Duration::from_secs(5));
218+
}
219+
220+
// 检查目标目录中是否存在 world_name
221+
while world_path.exists() {
222+
match fs::remove_dir_all(&world_path) {
223+
Ok(_) => {
224+
println!("Successfully deleted the world directory.");
225+
break;
226+
},
227+
Err(e) => {
228+
eprintln!("Failed to delete the world directory: {}. Retrying...", e);
229+
sleep(Duration::from_secs(3)); // 等待一秒后重试
230+
},
231+
}
232+
}
233+
234+
// 解压备份文件到目标目录,并命名为 world_name
235+
unzip_backup(&backup_path, &world_path)?;
236+
237+
// 启动服务器
238+
Command::new(&server_exe_path)
239+
.current_dir(target_dir) // 设置工作目录
240+
.spawn()?;
241+
242+
println!("Backup {} recovered and server started.", backup_path.display());
243+
244+
Ok(())
245+
}
246+
247+
fn terminate_server_process(server_exe_path: &Path) -> io::Result<()> {
248+
let output = Command::new("tasklist")
249+
.stdout(Stdio::piped())
250+
.spawn()?
251+
.wait_with_output()?;
252+
253+
let output_str = String::from_utf8_lossy(&output.stdout);
254+
255+
for line in output_str.lines() {
256+
if line.contains(server_exe_path.file_name().unwrap().to_str().unwrap()) {
257+
let parts: Vec<&str> = line.split_whitespace().collect();
258+
if let Some(pid) = parts.get(1) {
259+
Command::new("taskkill")
260+
.args(&["/PID", pid, "/F"])
261+
.spawn()?
262+
.wait()?;
263+
}
264+
}
265+
}
266+
267+
Ok(())
268+
}
269+
270+
#[tokio::main]
271+
async fn main() {
272+
let args: Vec<String> = env::args().collect();
273+
if args.len() < 2 {
274+
eprintln!("Usage: {} <operation> [additional arguments...]", args[0]);
275+
std::process::exit(1);
276+
}
277+
278+
let operation = &args[1];
279+
280+
match operation.as_str() {
281+
"copy" => {
282+
if args.len() != 4 {
283+
eprintln!("Usage for copy: {} copy <source> <destination>", args[0]);
284+
std::process::exit(1);
285+
}
286+
let source = Path::new(&args[2]);
287+
let destination = Path::new(&args[3]);
288+
match copy_dir_recursive(&source, &destination) {
289+
Ok(_) => println!("Copy completed successfully."),
290+
Err(e) => {
291+
eprintln!("Error during copy: {}", e);
292+
std::process::exit(1);
293+
}
294+
}
295+
}
296+
"cleanup" => {
297+
if args.len() != 4 {
298+
eprintln!("Usage for cleanup: {} cleanup <path> <max_age_days>", args[0]);
299+
std::process::exit(1);
300+
}
301+
302+
let path = Path::new(&args[2]);
303+
let max_age_days: u64 = args[3].parse().unwrap_or_else(|_| {
304+
eprintln!("Invalid max_age_days value");
305+
std::process::exit(1);
306+
});
307+
308+
if let Err(e) = delete_old_backups(&path, max_age_days) {
309+
eprintln!("Error during old backup cleanup: {}", e);
310+
std::process::exit(1);
311+
}
312+
}
313+
"recover" => {
314+
if args.len() != 6 {
315+
eprintln!("Usage for recover: {} recover <backup_file> <target_dir> <world_name> <server_exe>", args[0]);
316+
std::process::exit(1);
317+
}
318+
319+
let backup_file = Path::new(&args[2]);
320+
let target_dir = Path::new(&args[3]);
321+
let world_name = &args[4];
322+
let server_exe = &args[5];
323+
324+
if let Err(e) = recover_backup(&backup_file, &target_dir, world_name, server_exe) {
325+
eprintln!("Error during backup recovery: {}", e);
326+
std::process::exit(1);
327+
}
328+
}
329+
330+
"upload" => {
331+
if args.len() < 7 {
332+
eprintln!("Usage for upload: {} upload <backup_file> <remote_path> <webdav_url> <username> <password>", args[0]);
333+
std::process::exit(1);
334+
}
335+
336+
let backup_file = Path::new(&args[2]);
337+
let remote_path = &args[3];
338+
let webdav_url = &args[4];
339+
let username = &args[5];
340+
let password = &args[6];
341+
342+
if let Err(e) = upload_backup(backup_file, webdav_url, remote_path, username, password).await {
343+
eprintln!("Error during file upload: {}", e);
344+
std::process::exit(1);
345+
}
346+
}
347+
_ => {
348+
eprintln!("Unknown operation: {}", operation);
349+
std::process::exit(1);
350+
}
351+
}
352+
}

0 commit comments

Comments
 (0)