Handle and read (http Request<Body>) as a file, strings (csv)..?

// we receive
use axum::{
    http::{
        Request
    },
};
use axum::body;
// ..
    let buf = body::to_bytes(
        req.into_body(), usize::MAX
    ).await.unwrap();
------WebKitFormBoundaryczNUgk8xeotfVt41
Content-Disposition: form-data; name="url_f"; filename="import.csv"
Content-Type: text/csv

id,email,username
1,one@example.com,one,
2,three@example.com,three
3,four@example.com,four
4,five@example.com,five
5,two@example.com,two

------WebKitFormBoundaryczNUgk8xeotfVt41--

how or with what to process (the above) in order to read it in a standard way..

    let body = String::from_utf8(buf.to_vec()).unwrap();
    let mut rdr = Reader::from_reader(body.as_bytes());
    for result in rdr.deserialize() {
        let record: EpCsvUser = result.unwrap();
        println!("record .. {:?}", record);
    }

What you have here is a multipart body. Here are a few crates that can parse multipart content: ‘multipart’ search // Lib.rs. They should allow you to iterate over each part (separated by these boundary strings you see at the beginning and end of your snippet) and then you can handle them however you like, i.e in your case process the content of a single part as CSV data.

2 Likes

Previously, I did just that..

Could you share a little bit more of your code then? I'm not sure if I understood what your problem is correctly. Axum also has a Multipart extractor which I just discovered. Have you tried using that?

1 Like
use axum::{
    body::Body,
    extract::{State, Multipart},
    response::{IntoResponse, Redirect, Html},
    http::{
        // Request,
        Response, StatusCode,
    },
    Extension,
};

pub async fn export_users(
    State(pool): State<Pool>,
    mut multipart: Multipart
) -> impl IntoResponse {

    while let Some(field) = multipart.next_field().await.unwrap() {

        let data = field.bytes().await.unwrap();

        let body = String::from_utf8(data.to_vec()).unwrap();
        let mut rdr = Reader::from_reader(body.as_bytes());
        
        let mut conn = pool.get().await.unwrap();
        use schema::users::dsl::*;

        for result in rdr.deserialize() {
            let record: ExCsvUser = result.unwrap();
            println!("record .. {:?}", record);
            println!("result id .. {:?}", record.password);

            let salt = SaltString::generate(&mut OsRng);
            let pass = Pbkdf2.hash_password(
                record.password.as_bytes(), &salt
            );
            let hashed_password = match pass {
                Ok(pass) => pass.to_string(),
                Err(_) => "Err password".to_string(),
            };

            let new_user = CsvUser {
                id: record.id,
                email: record.email,
                username: record.username,
                password: hashed_password,
                created_at: Utc::now(),
                updated_at: Utc::now(),
            };
            diesel::insert_into(users)
                .values(new_user)
                .returning(CsvUser::as_returning())
                .get_result(&mut conn)
                .await.unwrap();
        }
    }

    return Redirect::to("/").into_response()
}

I think you forgot to post the error or describe what is not working.

1 Like