authbeam/
avif.rs

1use axum::{
2    body::Bytes,
3    extract::{FromRequest, Request},
4    http::{header::CONTENT_TYPE, StatusCode},
5};
6use axum_extra::extract::Multipart;
7use std::{fs::File, io::BufWriter};
8
9/// An image extractor accepting:
10/// * `multipart/form-data`
11/// * `image/png`
12/// * `image/jpeg`
13/// * `image/avif`
14/// * `image/webp`
15pub struct Image(pub Bytes);
16
17impl<S> FromRequest<S> for Image
18where
19    Bytes: FromRequest<S>,
20    S: Send + Sync,
21{
22    type Rejection = StatusCode;
23
24    async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
25        let Some(content_type) = req.headers().get(CONTENT_TYPE) else {
26            return Err(StatusCode::BAD_REQUEST);
27        };
28
29        let body = if content_type
30            .to_str()
31            .unwrap()
32            .starts_with("multipart/form-data")
33        {
34            let mut multipart = Multipart::from_request(req, state)
35                .await
36                .map_err(|_| StatusCode::BAD_REQUEST)?;
37
38            let Ok(Some(field)) = multipart.next_field().await else {
39                return Err(StatusCode::BAD_REQUEST);
40            };
41
42            field.bytes().await.map_err(|_| StatusCode::BAD_REQUEST)?
43        } else if (content_type == "image/avif")
44            | (content_type == "image/jpeg")
45            | (content_type == "image/png")
46            | (content_type == "image/webp")
47        {
48            Bytes::from_request(req, state)
49                .await
50                .map_err(|_| StatusCode::BAD_REQUEST)?
51        } else {
52            return Err(StatusCode::BAD_REQUEST);
53        };
54
55        Ok(Self(body))
56    }
57}
58
59/// Create an AVIF buffer given an input of `bytes`
60pub fn save_avif_buffer(path: &str, bytes: Vec<u8>) -> std::io::Result<()> {
61    let pre_img_buffer = match image::load_from_memory(&bytes) {
62        Ok(i) => i,
63        Err(_) => {
64            return Err(std::io::Error::new(
65                std::io::ErrorKind::InvalidData,
66                "Image failed",
67            ));
68        }
69    };
70
71    let file = File::create(path)?;
72    let mut writer = BufWriter::new(file);
73
74    if let Err(_) = pre_img_buffer.write_to(&mut writer, image::ImageFormat::Avif) {
75        return Err(std::io::Error::new(
76            std::io::ErrorKind::Other,
77            "Image conversion failed",
78        ));
79    };
80
81    Ok(())
82}