-
Notifications
You must be signed in to change notification settings - Fork 4
An example to store file in DB #10
Comments
I was so happy when I found this crate because I was struggling to implement a file upload via actix-web + actix-multipart natively. However, I'm also wondering how I can persist the Tempfile to disk. NamedTempFile has method persist(). When using persist(), it does not compile because of This is my code (taken from the example and modified): #[derive(MultipartForm)]
struct Upload {
text: Text<String>,
number: Text<i64>,
file: Tempfile,
}
#[post("/")]
async fn route(form: MultipartForm<Upload>) -> impl Responder {
let file = &form.file.file;
let _ = file.persist("/tmp/store/hugo.dat");
format!("Ok")
} error[E0507]: cannot move out of `*file` which is behind a shared reference
--> src/main.rs:18:13
|
18 | let _ = file.persist("/tmp/store/hugo.dat");
| ^^^^^------------------------------
| | |
| | `*file` moved due to this method call
| move occurs because `*file` has type `tempfile::file::NamedTempFile`, which does not implement the `Copy` trait
|
note: this function takes ownership of the receiver `self`, which moves `*file`
--> /home/tobias/.cargo/registry/src/github.com-1ecc6299db9ec823/tempfile-3.3.0/src/file/mod.rs:714:36
|
714 | pub fn persist<P: AsRef<Path>>(self, new_path: P) -> Result<File, PersistError> { Not sure if this is solvable. Any help appreciated. |
Hi @tokcum You need to have a mutable reference access to the form before you can mutate it. One way to do this is by calling the into_inner() function on the form. So then you should be able to do something like #[post("/")]
async fn route(form: MultipartForm<Upload>) -> impl Responder {
let mut form: Upload = form.into_inner();
let _ = form.file.file.persist("/tmp/store/hugo.dat");
} Alternatively (i haven't tested this) - but I think you should just be able to make the form mutable #[post("/")]
async fn route(mut form: MultipartForm<Upload>) -> impl Responder {
} |
Also, @tokcum, since it's a form, you can do (please refer to the comments in the code.): #[derive(MultipartForm)]
struct Upload {
text: Text<String>,
number: Text<i64>,
file: Tempfile,
}
#[post("/")]
async fn route(form: MultipartForm<Upload>) -> impl Responder {
// get the file name
let filename = form.0.file.file_name.as_ref().map(|m| m.as_ref()).unwrap_or("null");
// create the directory(ies) you want it saved in, if not previously created
// this will create a `tmp` directory in your project's root and `store` will be
// subdirectory
std::fs::create_dir_all("tmp/store/").expect("Unable to create folders");
// If you don't want file overwrites, you can prepend or affix some random strings to the
// filename
let random_bytes: String = {
let mut buff = [0_u8; 8];
OsRng.fill_bytes(&mut buff);
hex::encode(buff)
};
let filepath = format!("tmp/store/{}_{}", random_bytes, file_name);
// If you want it saved in the DB, it is better to save a refernce to the file location
// Like you could save something like https://localhost:8000/{filepath} to the DB
// Then to persist, you can just do
let _ = form.0.file.file.persist(filepath);
// But I prefer wrapping the persistence process in `block`.
let _ = actix_web::web::block(|| form.0.file.file.persist(filepath));
format!("Ok")
} You should be good with that. To use |
Thank you @jacob-pro for pointing out into_inner(). Btw, the mut is not required. So, this approach works:
|
@Sirneij, thanks for your approach. It works as well. I simplified it to show the essence. So with form.0, I don't even need the call to into_inner(). #[post("/")]
async fn route(form: MultipartForm<Upload>) -> impl Responder {
let _ = form.0.file.file.persist("/tmp/store/hugo.dat");
format!("Ok")
} |
Assuming you are working with a PostgreSQL database, any example of dropping the uploaded file in a
media
folder at the project's root and storing a reference to the file in DB?The text was updated successfully, but these errors were encountered: