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
9pub 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
59pub 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}