upload.rs 2.27 KB
use actix_web::{Error as ActixError, HttpResponse, web, http::StatusCode};
use anyhow::Result;
use async_std::fs::DirBuilder;
use futures::{stream::StreamExt, AsyncWriteExt};
use async_std::fs::OpenOptions;

use crate::{AppData, models::image::{Upload, self}, error::Error};
use crate::config::CONFIG;

pub async fn upload( app_data :web::Data<AppData>
                   , mut body :web::Payload
                   , request  :web::HttpRequest
                   ) -> Result<HttpResponse, ActixError>
{
    let pool = app_data.database_pool.clone();
    let worker = app_data.tx_upload_worker.clone();

    let upload_uuid = Some(uuid::Uuid::new_v4().as_bytes().to_vec());
    let size = request.headers().get("content-length")
             . and_then(|h| h.to_str().ok())
             . and_then(|s| s.parse::<i32>().ok());
    let mime_type = request.headers().get("content-type")
                  . and_then(|h| h.to_str().ok())
                  . ok_or(Error::new( "Upload expects content-type"
                                    , StatusCode::BAD_REQUEST ))?;
    let mime_type = String::from(mime_type);

    let mut upload = Upload {
        upload_uuid,
        size: size.unwrap_or(0),
        mime_type
    };

    DirBuilder::new() . recursive(true)
                      . create(CONFIG.upload_dir())
                      . await?;

    let mut upload_size = 0;
    let upload_filename = upload.upload_path().await.unwrap();
    let mut output = OpenOptions::new();
    let mut output = output
                   . create(true)
                   . write(true)
                   . open(&upload_filename).await?;
    while let Some(item) = body.next().await {
        let item = item?;
        output.write_all(&item).await?;
        upload_size += item.len() as i32;
    }
    output.flush().await?;

    if let Some(size) = size {
        if size != upload_size {
            Err(Error::new( "Did not receive expected size"
                          , StatusCode::BAD_REQUEST ))?
        }
    } else {
        upload.size = upload_size;
    }

    let pool_for_worker = pool.clone();
    let image = web::block(move || image::upload(pool, upload)).await?;
    worker . send((pool_for_worker, image.clone())).await
           . map_err(|e| Error::from(e))?;

    Ok(HttpResponse::Accepted().json(image))
}