use lazy_static::lazy_static;
use crate::prelude2::*;
use std::str::FromStr;
use chksum_md5 as md5;
use futures_util::TryStreamExt as _;
use actix_web::http::header;
use mongodb::bson::doc;
use crate::core::auth0::Requestor;
use crate::core::auth0::UserId;
use crate::sitepages::upload0::clear_files;
use crate::sitepages::upload0::UploadType;
use crate::sitepages::upload0::GRID_FS_DATABASE_NAME;
lazy_static! {
static ref LIMIT_VIDEO_FILE_COUNT: usize = 1; static ref LIMIT_VIDEO_FILE_SIZE: usize = 1_048_576 * 9500; static ref LEGAL_VIDEO_IMAGE_FILETYPES: [String; 1] = ["video/mp4".to_string()];
}
pub async fn upload_videos(
mut payload: actix_multipart::Multipart,
request: HttpRequest,
app_state: web::Data<AppContext>,
user_id: web::ReqData<UserId>,
requestor: web::ReqData<Requestor>,
) -> impl Responder {
let user_id = user_id.into_inner();
let user_details = requestor.user();
let content_length = match request.headers().get(header::CONTENT_LENGTH) {
Some(val) => val.to_str().unwrap_or("0").parse().unwrap(),
None => 0,
};
if content_length == 0 {
return request.json(200, R::failed(400, "视频文件为空!".to_string()));
}
if content_length > *LIMIT_VIDEO_FILE_SIZE {
while let Ok(Some(_)) = payload.try_next().await {
}
return request.json(
200,
R::failed(
400,
format!(
"视频大小超过限制: {}",
crate::commons::format_bytes_size(*LIMIT_VIDEO_FILE_SIZE)
),
),
);
}
let mut current_count = 0;
let mut upload_type: Option<String> = None;
let mut is_invalid = false;
let mut failed_message = String::new();
let mut failed_code = 0;
let mut upload_id = None;
let mut file_list = vec![];
loop {
if let Ok(Some(mut field)) = payload.try_next().await {
if is_invalid {
continue;
}
if current_count > *LIMIT_VIDEO_FILE_COUNT {
failed_code = 400;
failed_message =
format!("最大上传视频数量: {}", *LIMIT_VIDEO_FILE_COUNT).to_string();
is_invalid = true;
continue;
}
if field.name().is_none() {
continue;
}
let field_name = field.name().unwrap();
if field_name == "upload_type" {
while let Ok(Some(_chunk)) = field.try_next().await {
if let Ok(value) = std::str::from_utf8(&_chunk) {
upload_type = Some(value.to_string());
break;
}
}
continue;
}
if field_name != "file0" {
continue;
}
if let Some(file_type) = field.content_type() {
if !LEGAL_VIDEO_IMAGE_FILETYPES.contains(&file_type.to_string()) {
failed_code = 400;
failed_message = format!("不支持的视频类型: {}", &file_type);
log::warn!("{}", failed_message);
is_invalid = true;
continue;
}
} else {
continue;
}
let (file_name, destination, _) = crate::commons::save_upload(field).await?;
log::info!("save_upload: destination={}", destination.to_str().unwrap());
file_list.push((file_name, destination));
} else {
break;
}
current_count += 1;
}
if is_invalid {
clear_files(
file_list
.into_iter()
.map(|(_, path)| path.clone())
.collect(),
);
return request.json(200, R::failed(failed_code, failed_message));
}
if let Some(upload_type) = upload_type {
match UploadType::from_str(&upload_type) {
Ok(upload_type) => {
for (file_name, destination) in file_list {
let mime_type = mime_guess::MimeGuess::from_path(&destination)
.first()
.map(|mime| mime.to_string());
let file = std::fs::File::open(&destination).unwrap();
let file_md5 = md5::chksum(&file).unwrap().to_hex_lowercase();
let curr_fsize = file.metadata().unwrap().len();
if let Some((_id, flength)) = app_state
.mongo()
.get_file_by_md5(&GRID_FS_DATABASE_NAME, &file_md5)
.await?
{
if curr_fsize == flength {
log::info!(
"upload_file: _upload_type={}, file_md5={}, flength={}, destination={}",
upload_type.as_str(),
file_md5,
flength,
destination.to_str().unwrap()
);
if let Err(err) = std::fs::remove_file(&destination) {
log::error!("clear-files-error: error={:?}", err)
}
return request.json(200, R::ok(_id));
}
}
log::info!(
"upload_file: _upload_type={}, file_md5={}, file_size={}, destination={}",
upload_type.as_str(),
file_md5,
curr_fsize,
destination.to_str().unwrap()
);
let metadata = doc! {
"user_id": &user_id.0,
"resource_id": &user_details.resource_id,
"upload_type": upload_type.as_str(),
"mime_type": mime_type,
"created_at": crate::commons::timestamp_millis()
};
upload_id = app_state
.mongo()
.save_file_with_opts(
&GRID_FS_DATABASE_NAME,
&file_name,
&destination,
metadata,
)
.await?;
std::fs::remove_file(destination).map_err(|e| anyhow::anyhow!(e))?;
}
request.json(200, R::ok(upload_id))
}
Err(_) => {
clear_files(
file_list
.into_iter()
.map(|(_, path)| path.clone())
.collect(),
);
request.json(
200,
R::failed(
failed_code,
format!("不支持的视频上传类型: {}", upload_type),
),
)
}
}
} else {
clear_files(
file_list
.into_iter()
.map(|(_, path)| path.clone())
.collect(),
);
request.json(
200,
R::failed(failed_code, "upload_type is required!".to_string()),
)
}
}