diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2b73450 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.checkOnSave": false +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 898df92..d299eda 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,7 @@ async fn main() { let app = Router::new() .route("/", get(|| async move { "welcome to Image upload api" })) .route("/upload", post(upload_image)) + .route("/upload/master", post(upload_image_no_timestamp)) .layer(cors_layer) .layer(Extension(aws_s3_client)) .layer(axum::middleware::from_fn(log_request_ip)); @@ -113,3 +114,63 @@ async fn upload_image( // send the urls in response Ok(Json(serde_json::json!(res))) } + + +async fn upload_image_no_timestamp( + Extension(s3_client): Extension, + mut files: Multipart, +) -> Result, (StatusCode, Json)> { + // get the name of aws bucket from env variable + let bucket = std::env::var("AWS_S3_BUCKET").unwrap_or_else(|_| "my-bucket-name".to_owned()); + // if you have a public url for your bucket, place it as ENV variable BUCKET_URL + let bucket_url = std::env::var("BUCKET_URL").unwrap_or_else(|_| bucket.to_owned()); + // if a sub folder path is set in environment then get that else set a default sub path + let sub_path = std::env::var("BUCKET_SUB_PATH").unwrap_or_else(|_| "uploaded_images".to_owned()); + // we are going to store the respose in HashMap as filename: url => key: value + let mut res = HashMap::new(); + while let Some(file) = files.next_field().await.unwrap() { + // this is the name which is sent in formdata from frontend or whoever called the api, i am + // using it as category, we can get the filename from file data + let category = file.name().unwrap().to_string(); + // name of the file with extention + let name = file.file_name().unwrap().to_string(); + // file data + let data = file.bytes().await.unwrap(); + // the request can control where to save the image on AWS S3 + let request_path = if category.trim().is_empty() {"".to_owned()} else {format!("{}/", &category.trim_end_matches('/'))}; + // the path of file to store on aws s3 with file name and extention + // timestamp_category_filename => 14-12-2022_01:01:01_customer_somecustomer.jpg + let key = format!( + "{}/images/{}_{}_{}", + sub_path, + &request_path, + &category, + &name + ); + + // send Putobject request to aws s3 + let _resp = s3_client + .put_object() + .bucket(&bucket) + .key(&key) + .body(data.into()) + .send() + .await + .map_err(|err| { + dbg!(err); + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(serde_json::json!({"err": "an error occured during image upload"})), + ) + })?; + dbg!(_resp); + res.insert( + // concatinating name and category so even if the filenames are same it will not + // conflict + format!("{}_{}", &name, &category), + format!("{}/{}", bucket_url, key), + ); + } + // send the urls in response + Ok(Json(serde_json::json!(res))) +}