@@ -37,6 +37,7 @@ async fn main() {
3737 let app = Router :: new ( )
3838 . route ( "/" , get ( || async move { "welcome to Image upload api" } ) )
3939 . route ( "/upload" , post ( upload_image) )
40+ . route ( "/upload/master" , post ( upload_image_no_timestamp) )
4041 . layer ( cors_layer)
4142 . layer ( Extension ( aws_s3_client) )
4243 . layer ( axum:: middleware:: from_fn ( log_request_ip) ) ;
@@ -113,3 +114,63 @@ async fn upload_image(
113114 // send the urls in response
114115 Ok ( Json ( serde_json:: json!( res) ) )
115116}
117+
118+
119+ async fn upload_image_no_timestamp (
120+ Extension ( s3_client) : Extension < Client > ,
121+ mut files : Multipart ,
122+ ) -> Result < Json < serde_json:: Value > , ( StatusCode , Json < serde_json:: Value > ) > {
123+ // get the name of aws bucket from env variable
124+ let bucket = std:: env:: var ( "AWS_S3_BUCKET" ) . unwrap_or_else ( |_| "my-bucket-name" . to_owned ( ) ) ;
125+ // if you have a public url for your bucket, place it as ENV variable BUCKET_URL
126+ let bucket_url = std:: env:: var ( "BUCKET_URL" ) . unwrap_or_else ( |_| bucket. to_owned ( ) ) ;
127+ // if a sub folder path is set in environment then get that else set a default sub path
128+ let sub_path = std:: env:: var ( "BUCKET_SUB_PATH" ) . unwrap_or_else ( |_| "uploaded_images" . to_owned ( ) ) ;
129+ // we are going to store the respose in HashMap as filename: url => key: value
130+ let mut res = HashMap :: new ( ) ;
131+ while let Some ( file) = files. next_field ( ) . await . unwrap ( ) {
132+ // this is the name which is sent in formdata from frontend or whoever called the api, i am
133+ // using it as category, we can get the filename from file data
134+ let category = file. name ( ) . unwrap ( ) . to_string ( ) ;
135+ // name of the file with extention
136+ let name = file. file_name ( ) . unwrap ( ) . to_string ( ) ;
137+ // file data
138+ let data = file. bytes ( ) . await . unwrap ( ) ;
139+ // the request can control where to save the image on AWS S3
140+ let request_path = if category. trim ( ) . is_empty ( ) { "" . to_owned ( ) } else { format ! ( "{}/" , & category. trim_end_matches( '/' ) ) } ;
141+ // the path of file to store on aws s3 with file name and extention
142+ // timestamp_category_filename => 14-12-2022_01:01:01_customer_somecustomer.jpg
143+ let key = format ! (
144+ "{}/images/{}_{}_{}" ,
145+ sub_path,
146+ & request_path,
147+ & category,
148+ & name
149+ ) ;
150+
151+ // send Putobject request to aws s3
152+ let _resp = s3_client
153+ . put_object ( )
154+ . bucket ( & bucket)
155+ . key ( & key)
156+ . body ( data. into ( ) )
157+ . send ( )
158+ . await
159+ . map_err ( |err| {
160+ dbg ! ( err) ;
161+ (
162+ StatusCode :: INTERNAL_SERVER_ERROR ,
163+ Json ( serde_json:: json!( { "err" : "an error occured during image upload" } ) ) ,
164+ )
165+ } ) ?;
166+ dbg ! ( _resp) ;
167+ res. insert (
168+ // concatinating name and category so even if the filenames are same it will not
169+ // conflict
170+ format ! ( "{}_{}" , & name, & category) ,
171+ format ! ( "{}/{}" , bucket_url, key) ,
172+ ) ;
173+ }
174+ // send the urls in response
175+ Ok ( Json ( serde_json:: json!( res) ) )
176+ }
0 commit comments