@@ -277,12 +277,6 @@ async fn upload_file(
277277 return ExitCode :: Success ;
278278 }
279279
280- // Determine content type
281- let guessed_type: Option < String > = mime_guess:: from_path ( src)
282- . first ( )
283- . map ( |m| m. essence_str ( ) . to_string ( ) ) ;
284- let content_type = args. content_type . as_deref ( ) . or ( guessed_type. as_deref ( ) ) ;
285-
286280 // Get file size for progress bar decision
287281 let file_size = match std:: fs:: metadata ( src) {
288282 Ok ( m) => m. len ( ) ,
@@ -294,6 +288,16 @@ async fn upload_file(
294288 }
295289 } ;
296290
291+ // Determine content type
292+ let guessed_type: Option < String > = mime_guess:: from_path ( src)
293+ . first ( )
294+ . map ( |m| m. essence_str ( ) . to_string ( ) ) ;
295+ let content_type = select_upload_content_type (
296+ args. content_type . as_deref ( ) ,
297+ guessed_type. as_deref ( ) ,
298+ file_size,
299+ ) ;
300+
297301 // Show progress bar for large files
298302 let progress = if file_size >= MULTIPART_THRESHOLD {
299303 tracing:: debug!(
@@ -335,6 +339,18 @@ async fn upload_file(
335339 }
336340}
337341
342+ fn select_upload_content_type < ' a > (
343+ explicit_type : Option < & ' a str > ,
344+ guessed_type : Option < & ' a str > ,
345+ file_size : u64 ,
346+ ) -> Option < & ' a str > {
347+ if file_size > MULTIPART_THRESHOLD {
348+ explicit_type
349+ } else {
350+ explicit_type. or ( guessed_type)
351+ }
352+ }
353+
338354async fn upload_directory (
339355 client : & S3Client ,
340356 src : & Path ,
@@ -862,6 +878,33 @@ mod tests {
862878 assert ! ( !progress. is_visible( ) ) ;
863879 }
864880
881+ #[ test]
882+ fn test_select_upload_content_type_uses_guess_for_small_files ( ) {
883+ let selected =
884+ select_upload_content_type ( None , Some ( "text/plain" ) , MULTIPART_THRESHOLD - 1 ) ;
885+
886+ assert_eq ! ( selected, Some ( "text/plain" ) ) ;
887+ }
888+
889+ #[ test]
890+ fn test_select_upload_content_type_skips_guess_for_multipart_files ( ) {
891+ let selected =
892+ select_upload_content_type ( None , Some ( "text/plain" ) , MULTIPART_THRESHOLD + 1 ) ;
893+
894+ assert_eq ! ( selected, None ) ;
895+ }
896+
897+ #[ test]
898+ fn test_select_upload_content_type_keeps_explicit_type_for_multipart_files ( ) {
899+ let selected = select_upload_content_type (
900+ Some ( "application/octet-stream" ) ,
901+ Some ( "text/plain" ) ,
902+ MULTIPART_THRESHOLD + 1 ,
903+ ) ;
904+
905+ assert_eq ! ( selected, Some ( "application/octet-stream" ) ) ;
906+ }
907+
865908 #[ test]
866909 fn test_parse_cp_path_prefers_existing_local_path_when_alias_missing ( ) {
867910 let ( alias_manager, temp_dir) = temp_alias_manager ( ) ;
0 commit comments