use lazy_static::lazy_static;
use crate::prelude2::*;
use std::str::FromStr;
use actix_web::http::header;
use futures_util::TryStreamExt as _;
use mongodb::bson::doc;
use mime::Mime;
use mime::IMAGE_GIF;
use mime::IMAGE_JPEG;
use mime::IMAGE_PNG;
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_IMAGE_FILE_COUNT: usize = 1; static ref LIMIT_FILE_SIZE: usize = 1_048_576 * 150; static ref LEGAL_IMAGE_FILETYPES: [Mime; 3] = [IMAGE_PNG, IMAGE_JPEG, IMAGE_GIF];
}
pub async fn upload_images(
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_FILE_SIZE {
while let Ok(Some(_)) = payload.try_next().await {
}
return request.json(
200,
R::failed(
400,
format!(
"图片大小超过限制: {}",
crate::commons::format_bytes_size(*LIMIT_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![];
let mut field_file0_found = false;
loop {
if let Ok(Some(mut field)) = payload.try_next().await {
if is_invalid {
continue;
}
if current_count > *LIMIT_IMAGE_FILE_COUNT {
failed_code = 400;
failed_message =
format!("最大上传图片数量: {}", *LIMIT_IMAGE_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;
}
field_file0_found = true;
let content_disposition = field.content_disposition();
let mut filename = "unknown";
if content_disposition.is_some() {
if let Some(f) = content_disposition {
filename = f.get_filename().unwrap_or("unknown");
}
}
if let Some(file_type) = field.content_type() {
if !LEGAL_IMAGE_FILETYPES.contains(file_type) {
failed_code = 400;
failed_message =
format!("不支持的图片类型: {}, file_name={}", &file_type, filename);
log::warn!("{}", failed_message);
is_invalid = true;
continue;
}
} else {
continue;
}
let (file_name, destination, _) = crate::commons::save_upload(field).await?;
file_list.push((file_name, destination));
} else {
break;
}
current_count += 1;
}
if is_invalid || !field_file0_found {
clear_files(
file_list
.into_iter()
.map(|(_, path)| path.clone())
.collect(),
);
if !field_file0_found {
return request.json(200, R::failed(400, "file0 is required"));
} else {
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());
log::info!(
"upload_file: _upload_type={}, destination={}",
upload_type.as_str(),
destination.to_str().unwrap()
);
if upload_type == UploadType::ProfilePicture {
}
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()),
)
}
}