Help with Actix Web upload and convert files to PDF

Hi everyone, I would like some help with my small implementation of a tool using actix-web, is a simple PDF converter is working as expected if the web::block isn't used on the file reading, but when implementing the same code using the web::block an error happens on compile time (not sure why), the project is a simple server with a route that the user can upload some Docx file and returns a PDF, also would be awesome to have some reviews and where it's possible to improve that code.

use std::fs::File;
use std::io::{prelude::*, BufReader, Write};
use std::process::Command;

use actix_multipart::Multipart;
use actix_web::{post, web, App, Error, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};
use tempdir::TempDir;

#[post("/")]
async fn index(mut payload: Multipart) -> Result<HttpResponse, Error> {
    let dir = TempDir::new("temp")?;
    let mut vec: Vec<u8> = vec![];

    while let Ok(Some(mut field)) = payload.try_next().await {
        let content_type = field.content_disposition().unwrap();
        let filename = content_type.get_filename().unwrap();

        let tmp_filepath = dir.path().join(sanitize_filename::sanitize(&filename));
        let mut filepath = tmp_filepath.clone();

        let mut f = web::block(move || std::fs::File::create(&tmp_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?;
        }

        println!("{:?}", &filepath);

        let mut soffice = Command::new("soffice");
        let result = soffice
            .arg("--headless")
            .arg("--convert-to")
            .arg("pdf")
            .arg(&filepath)
            .arg("--outdir")
            .arg(std::path::Path::new(&filepath).parent().unwrap())
            .output()?;

        println!("{}", String::from_utf8_lossy(&result.stdout));

        filepath.set_extension("pdf");

        // Error happens on web::block here
        vec = web::block(move || {
            let file = File::open(filepath).unwrap();
            let mut buf_reader = BufReader::new(file);
            let mut tmp_vec: Vec<u8> = vec![];
            buf_reader.read_to_end(&mut tmp_vec)?;
            Ok(tmp_vec)
        })
        .await?;
    }

    Ok(HttpResponse::Ok()
        .content_type("application/pdf")
        .body(vec)
        .into())
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    let port = std::env::var("PORT").unwrap_or("4701".to_string());

    let server_address = format!("0.0.0.0:{}", port);
    println!("Serving on port {}", server_address);

    HttpServer::new(|| App::new().service(index))
        .bind(server_address)?
        .run()
        .await
}

The error

error[E0698]: type inside `async fn` body must be known in this context
  --> src/main.rs:49:15
   |
49 |         vec = web::block(move || {
   |               ^^^^^^^^^^ cannot infer type for type parameter `E` declared on the function `block`
   |
note: the type is part of the `async fn` body because of this `await`
  --> src/main.rs:49:15
   |
49 |           vec = web::block(move || {
   |  _______________^
50 | |             let file = File::open(filepath).unwrap();
51 | |             let mut buf_reader = BufReader::new(file);
52 | |             let mut tmp_vec: Vec<u8> = vec![];
...  |
55 | |         })
56 | |         .await?;
   | |______________^

error: aborting due to previous error

Tell it the error type returned by the block call.

Result::<_, std::io::Error>::Ok(tmp_vec)
// or
std::io::Result::Ok(tmp_vec)

The issue is that question marks try to convert the error, so if we let E be the error type in question, it knows that read_to_end's error converts into E, and that E converts into actix_web::Error, but this is not enough to pin down E.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.