use lazy_static::lazy_static;
use crate::prelude2::*;
use std::collections::HashMap;
use std::str::FromStr;
use futures_util::stream::StreamExt;
pub mod upload_files;
pub mod upload_images;
pub mod upload_videos;
pub use upload_images::*;
pub use upload_videos::*;
lazy_static! {
pub static ref TEMP_UPLOAD_FOLDER: String = crate::commons::upload_folder();
pub static ref GRID_FS_DATABASE_NAME: String = String::from("mygridfs");
}
#[repr(usize)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub enum UploadType {
ProfilePicture,
IdCardPicture1a,
IdCardPicture2b,
CertificatePicture,
DiplomaPicture,
SimpleUploader,
}
pub static UPLOAD_TYPE_NAMES: [&str; 6] = [
"profile_picture", "idcard_picture1a", "idcard_picture2a", "certificate_picture", "diploma_picture", "simple_uploader", ];
impl UploadType {
fn from_usize(u: usize) -> Option<UploadType> {
match u {
0 => Some(UploadType::ProfilePicture),
1 => Some(UploadType::IdCardPicture1a),
2 => Some(UploadType::IdCardPicture2b),
3 => Some(UploadType::CertificatePicture),
4 => Some(UploadType::DiplomaPicture),
5 => Some(UploadType::SimpleUploader),
_ => None,
}
}
#[inline]
pub fn max() -> UploadType {
UploadType::ProfilePicture
}
pub fn as_str(&self) -> &'static str {
UPLOAD_TYPE_NAMES[*self as usize]
}
pub fn iter() -> impl Iterator<Item = Self> {
(0..7).map(|i| Self::from_usize(i).unwrap())
}
}
#[allow(missing_copy_implementations)]
#[derive(Debug, PartialEq, Eq)]
pub struct ParseUploadError(());
fn ok_or<T, E>(t: Option<T>, e: E) -> core::result::Result<T, E> {
match t {
Some(t) => Ok(t),
None => Err(e),
}
}
impl FromStr for UploadType {
type Err = ParseUploadError;
fn from_str(types: &str) -> core::result::Result<UploadType, Self::Err> {
ok_or(
UPLOAD_TYPE_NAMES
.iter()
.position(|&name| name.eq_ignore_ascii_case(types))
.map(|p| UploadType::from_usize(p).unwrap()),
ParseUploadError(()),
)
}
}
pub async fn get_file_by_md5(
query: web::Query<HashMap<String, String>>,
request: HttpRequest,
ctx: web::Data<AppContext>,
) -> impl Responder {
if let Some(md5) = query.get("md5") {
if let Ok(Some((_id, flength))) = ctx
.mongo()
.get_file_by_md5(&GRID_FS_DATABASE_NAME, md5)
.await
{
request.json(200, R::ok((_id, flength.to_string())))
} else {
request.json(200, R::ok(Vec::<String>::new()))
}
} else {
request.json(200, R::ok(Vec::<String>::new()))
}
}
pub async fn file_upload_form(
query: web::Query<HashMap<String, String>>,
request: HttpRequest,
app_state: web::Data<AppContext>,
) -> impl Responder {
if let Some(id) = query.get("id") {
if let Some(content_type) = app_state
.mongo()
.get_file_content_type(&GRID_FS_DATABASE_NAME, id)
.await?
{
let mut ctx = tera::Context::new();
if content_type == "video/mp4" {
ctx.insert("video_id", &id);
ctx.insert("mime_type", &content_type);
} else if content_type == "image/jpeg"
|| content_type == "image/jpg"
|| content_type == "image/png"
|| content_type == "image/gif"
{
ctx.insert("image_id", &id);
ctx.insert("mime_type", &content_type);
} else {
log::warn!(
"file_upload_form: id={}, message={}",
id,
"not support file mime type!"
)
}
return request.render(200, "upload0/file_upload.html", ctx);
}
request.render(200, "defaults/404.html", tera::Context::new())
} else {
request.render(200, "upload0/file_upload.html", tera::Context::new())
}
}
pub async fn image_viewer(
image_id: actix_web::web::Path<String>,
_request: HttpRequest,
app_state: web::Data<AppContext>,
) -> actix_web::Result<HttpResponse, actix_web::Error> {
match app_state
.mongo()
.get_file(&GRID_FS_DATABASE_NAME, &image_id.to_string())
.await
{
Ok((mime_type, mut _stream)) => {
let stream = async_stream::stream! {
while let Some(chunk) = _stream.next().await {
yield Ok::<_, actix_web::Error>(web::Bytes::from(chunk));
}
};
let mut response = HttpResponse::Ok();
match mime_type {
Some(mime_type) => {
response.content_type(mime_type);
}
None => {
response.content_type("image/jpeg");
}
}
Ok(response.streaming(stream))
}
Err(e) => Ok(HttpResponse::BadRequest().json(R::failed(400, e.to_string()))),
}
}
pub async fn download() -> actix_web::Result<HttpResponse, actix_web::Error> {
let chunk1: Vec<u8> = vec![65, 66, 67];
let chunk2: Vec<u8> = vec![68, 69, 70];
let chunk3: Vec<u8> = vec![71, 72, 73];
let stream = futures_util::stream::iter(vec![chunk1, chunk2, chunk3])
.map(|chunk| Ok::<_, actix_web::Error>(web::Bytes::from(chunk)))
.boxed();
let content_type = "text/plain";
let response = HttpResponse::Ok()
.content_type(content_type)
.streaming(stream);
Ok(response)
}
pub fn clear_files(file_list: Vec<std::path::PathBuf>) {
for destination in file_list {
if let Err(err) = std::fs::remove_file(destination) {
log::error!("clear-files-error: error={:?}", err)
}
}
}