mirror of
https://github.com/MeiK2333/river.git
synced 2025-11-04 14:49:40 +08:00
添加上传文件接口
This commit is contained in:
parent
664564e79b
commit
6eb1448a3d
@ -21,6 +21,7 @@ nix = "0.19.0"
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
zip = "0.5"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build ={ version = "0.3" }
|
tonic-build ={ version = "0.3" }
|
||||||
|
|||||||
4
judger/.gitignore
vendored
Normal file
4
judger/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!data
|
||||||
|
!run
|
||||||
4
judger/data/.gitignore
vendored
Normal file
4
judger/data/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!1000
|
||||||
|
!1001
|
||||||
4
judger/data/1000/.gitignore
vendored
Normal file
4
judger/data/1000/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!*.in
|
||||||
|
!*.out
|
||||||
1
judger/data/1000/1.in
Normal file
1
judger/data/1000/1.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 2
|
||||||
1
judger/data/1000/1.out
Normal file
1
judger/data/1000/1.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
3
|
||||||
4
judger/data/1001/.gitignore
vendored
Normal file
4
judger/data/1001/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!*.in
|
||||||
|
!*.out
|
||||||
0
judger/data/1001/1.in
Normal file
0
judger/data/1001/1.in
Normal file
1
judger/data/1001/1.out
Normal file
1
judger/data/1001/1.out
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello World!
|
||||||
2
judger/run/.gitignore
vendored
Normal file
2
judger/run/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
48
src/error.rs
Normal file
48
src/error.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#![macro_use]
|
||||||
|
|
||||||
|
use libc::strerror;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io;
|
||||||
|
use std::result;
|
||||||
|
use zip;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
IOError(io::Error),
|
||||||
|
ZipError(zip::result::ZipError),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = result::Result<T, Error>;
|
||||||
|
|
||||||
|
// 创建一个简单的包装
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! try_io {
|
||||||
|
($expression:expr) => {
|
||||||
|
match $expression {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(e) => return Err(Error::IOError(e)),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Error::IOError(ref e) => write!(f, "IOError: {}", errno_str(e.raw_os_error())),
|
||||||
|
_ => write!(f, "{:?}", self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn errno_str(errno: Option<i32>) -> String {
|
||||||
|
match errno {
|
||||||
|
Some(no) => {
|
||||||
|
let stre = unsafe { strerror(no) };
|
||||||
|
let c_str: &CStr = unsafe { CStr::from_ptr(stre) };
|
||||||
|
c_str.to_str().unwrap().to_string()
|
||||||
|
}
|
||||||
|
_ => "Unknown Error!".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/file.rs
Normal file
89
src/file.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use super::error::{Error, Result};
|
||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
use std::path::Path;
|
||||||
|
use tempfile::tempfile;
|
||||||
|
use zip;
|
||||||
|
|
||||||
|
pub fn extract(filedir: &Path, body: &Vec<u8>) -> Result<()> {
|
||||||
|
// 如果文件夹已存在,则删除再重新创建
|
||||||
|
if filedir.exists() {
|
||||||
|
try_io!(fs::remove_dir_all(&filedir));
|
||||||
|
}
|
||||||
|
try_io!(fs::create_dir(&filedir));
|
||||||
|
let mut file = try_io!(tempfile());
|
||||||
|
try_io!(file.write_all(&body));
|
||||||
|
|
||||||
|
let mut archive = match zip::ZipArchive::new(file) {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(e) => return Err(Error::ZipError(e)),
|
||||||
|
};
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
let mut file = match archive.by_index(i) {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(e) => return Err(Error::ZipError(e)),
|
||||||
|
};
|
||||||
|
let outpath = match file.enclosed_name() {
|
||||||
|
Some(path) => filedir.join(path).to_owned(),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (&*file.name()).ends_with('/') {
|
||||||
|
try_io!(fs::create_dir_all(&outpath));
|
||||||
|
} else {
|
||||||
|
if let Some(p) = outpath.parent() {
|
||||||
|
if !p.exists() {
|
||||||
|
try_io!(fs::create_dir_all(&p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut outfile = try_io!(fs::File::create(&outpath));
|
||||||
|
try_io!(io::copy(&mut file, &mut outfile));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mode) = file.unix_mode() {
|
||||||
|
try_io!(fs::set_permissions(
|
||||||
|
&outpath,
|
||||||
|
fs::Permissions::from_mode(mode)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test1() {
|
||||||
|
let filename = "hello.txt";
|
||||||
|
let zipfile = "hello.zip";
|
||||||
|
let prefix = "hello";
|
||||||
|
let mut file = fs::File::create(&filename).unwrap();
|
||||||
|
let _ = file.write_all(b"Hello World!").unwrap();
|
||||||
|
let _ = Command::new("zip")
|
||||||
|
.arg(&zipfile)
|
||||||
|
.arg(&filename)
|
||||||
|
.output()
|
||||||
|
.expect("failed to execute process");
|
||||||
|
// remove file
|
||||||
|
fs::remove_file(&filename).unwrap();
|
||||||
|
|
||||||
|
// extract
|
||||||
|
let body = fs::read(&zipfile).unwrap();
|
||||||
|
extract(&Path::new(&prefix.to_string()), &body).unwrap();
|
||||||
|
fs::remove_file(&zipfile).unwrap();
|
||||||
|
|
||||||
|
// check file
|
||||||
|
assert!(Path::new(&prefix).join(&filename).exists());
|
||||||
|
|
||||||
|
let body = fs::read_to_string(Path::new(&prefix).join(&filename)).unwrap();
|
||||||
|
assert_eq!(body, "Hello World!");
|
||||||
|
|
||||||
|
fs::remove_dir_all(&prefix).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/main.rs
29
src/main.rs
@ -1,16 +1,19 @@
|
|||||||
#![recursion_limit = "512"]
|
#![recursion_limit = "256"]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
use futures::StreamExt;
|
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use river::river_server::{River, RiverServer};
|
use river::river_server::{River, RiverServer};
|
||||||
use river::{JudgeRequest, JudgeResponse, UploadFile, UploadState};
|
use river::{JudgeRequest, JudgeResponse, UploadFile, UploadState};
|
||||||
|
use std::path::Path;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use tonic::transport::Server;
|
use tonic::transport::Server;
|
||||||
use tonic::{Request, Response, Status};
|
use tonic::{Request, Response, Status};
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
mod file;
|
||||||
|
|
||||||
pub mod river {
|
pub mod river {
|
||||||
tonic::include_proto!("river");
|
tonic::include_proto!("river");
|
||||||
}
|
}
|
||||||
@ -22,9 +25,25 @@ pub struct RiverService {}
|
|||||||
impl River for RiverService {
|
impl River for RiverService {
|
||||||
type JudgeStream =
|
type JudgeStream =
|
||||||
Pin<Box<dyn Stream<Item = Result<JudgeResponse, Status>> + Send + Sync + 'static>>;
|
Pin<Box<dyn Stream<Item = Result<JudgeResponse, Status>> + Send + Sync + 'static>>;
|
||||||
async fn upload(&self, _request: Request<UploadFile>) -> Result<Response<UploadState>, Status> {
|
|
||||||
let state = river::UploadState {
|
// 上传文件接口
|
||||||
state: Some(river::upload_state::State::Filepath("Success".to_string())),
|
async fn upload(&self, request: Request<UploadFile>) -> Result<Response<UploadState>, Status> {
|
||||||
|
let upload_file = request.into_inner();
|
||||||
|
|
||||||
|
// 文件放在 judger/data/ 目录下
|
||||||
|
let prefix_path = Path::new("judger/data/");
|
||||||
|
let path = prefix_path.join(&upload_file.filepath);
|
||||||
|
|
||||||
|
let result = file::extract(&path, &upload_file.data);
|
||||||
|
let state = match result {
|
||||||
|
Ok(_) => river::UploadState {
|
||||||
|
state: Some(river::upload_state::State::Filepath(
|
||||||
|
upload_file.filepath.to_string(),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
Err(e) => river::UploadState {
|
||||||
|
state: Some(river::upload_state::State::Errmsg(format!("{}", e))),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
Ok(Response::new(state))
|
Ok(Response::new(state))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user