This is working code to save a file in Actix when receiving a Multipart:
while let Ok(Some(mut field)) = payload.try_next().await {
let filepath = format!(".{}", file_path);
// File::create is blocking operation, use threadpool
let mut f = web::block(|| std::fs::File::create(filepath))
.await
.unwrap();
// Field in turn is stream of *Bytes* object
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();
// filesystem operations are blocking, we have to use threadpool
f = web::block(move || f.write_all(&data).map(|_| f))
.await
.unwrap();
}
}
Basing myself on that code, I tried creating the following method:
pub async fn save_file(
mut file_bytes: actix_web::web::Bytes,
file_path: &String,
) -> Option<bool> {
let filepath = format!(".{}", file_path);
let mut f = web::block(|| std::fs::File::create(filepath))
.await
.unwrap();
f = web::block(move || f.write_all(&file_bytes).map(|_| f))
.await
.unwrap();
Some(true)
}
Basically, I want to extract the data in actix_web::web::Bytes
format while looping over my Multipart. And when the loop reaches the file-field (which is an image) I want to execute the save_file
function.
This function works: It makes a new file, and the file has some content, but I can't open the JPEG because the file is corrupted or in a wrong format.
A small piece of the JPEG-file in text-editor:
Æ™ˆfl•r}ü%#ï=QÂjEÈñ¶:¸=ȱ8Ω‡«x_7fi–fl@ßüÍ‚XWÕıh~f‚DÇáP˛r≥foea'ª{¥ ˛[πù?$∑£¥£3¸|'∂œ±(⁄‘ì`rowØ<äÏœ,…ÍÓ.Å•‹™,d*=ÒıŒŒ◊‰+2YÑ-¨ëL2oq]'î!®ˆ-O§ ªd’S#µÕÕ|>ôOá∆7Ô¸øöõôùõs©|G˚ã0¿◊m†?MT=Ç~˛ÅK€Sµ•l"[®Ofi0C!]óŸœeFË}À*¿ :ß÷ßR-∑∂K€È±˜ÁœÔ<üd BÖG–ËI˘À¯Á†Õı±˘Åöû÷¸Ãã‚ûèèVVÓ|‰Ëˇ¶›Å˚W ˙·J/=h˝ªxëÒfl‰éɘEó¥˜y√!æ„Ì0ïœÒø÷3¸%_Ñ€˚ø@+wnAÊ˝†oÄfl™¸RÖ˙6ê"º~7ˇq`ó"˜!ı∆1≈gKõ‚<P:√™}¬NÀ®#¸yw~Zã±Lo«O›=ºÚÔ¯HœË∑‹)_—‘3‚qÑ*üàËO;Åˇ1)∫œ+eªÍà˛ònÕÃ.LM˝∫‹ó+ï»bP˛W^øæë…d≤ô\.√W°`1Ñø&'rˇc&¿ˇYπPªˇØaâ,‡?J\˙Á´ÔÔ’fg%›fl‡ñÉ›1Ùfl¶ó€
NˇoÔ¨,ˆoófj3 "˚!á˝:Éé¢ü»˛,’˚p["j@Ö?D˙óZMˇ…ù4˝œQømˇn _ò>EóÛó"ÌÉÄÕnmG˘ˇ{¯ˇÙ{≤q:¯‚Ù¢Õ–œÓë˝xG7)›õ›#\mπ.Sí˚Î;ßs%©øÊ+˙Äø“ë–J˚fiX•«ºmÁ2z’≥™ı’ÇÈ}̬^NaCx`*_uÇġ…
jÁ°Ì‚¨agCvÈÌÜû9F$¢„ˆ^T˝x•_’ãˇ¨¿üß{5Oa7©®_V˝lˆ˙ü{áƒgWõ?¸2∆˚
¨–3&µtéD
// CONTINUES LIKE THIS
This is how I call the save_file
function when receiving my Multipart:
let mut field_name = "".to_string();
while let Some(item) = payload.next().await {
let mut field = item?;
field_name = field
.content_disposition()
.unwrap()
.get_name()
.unwrap()
.to_string();
let mut field_data = "".to_string();
let mut byte_data: Option<actix_web::web::Bytes> = None;
while let Some(chunk) = field.next().await {
if field_name == "file".to_string() {
byte_data = Some(chunk.unwrap());
} else {
match std::str::from_utf8(&chunk?) {
Ok(data) => {
field_data = data.to_string();
}
Err(_) => {}
}
}
}
match &*field_name {
"name" => form.name = field_data,
"email" => form.email = field_data,
// ...
"file" => {
let file_name = format!(
"/uploads/business-images/{}.jpg",
form.name.to_lowercase().replace(" ", "-"),
);
// CALLED HERE
files::save_file(byte_data.unwrap(), &file_name).await;
}
_ => {
println!("Invalid");
}
}
}
How do I write the actix_web::web::Bytes
into a file with the correct format without corrupting it? I don't understand that the code works when looping over the Multipart, but not when I try to execute it outside of a loop, passing the Bytes directly...