fedimovies/src/utils/files.rs
2021-09-14 12:24:05 +00:00

69 lines
2 KiB
Rust

use std::fs::File;
use std::io::prelude::*;
use std::path::PathBuf;
use mime_guess::get_mime_extensions_str;
use mime_sniffer::MimeTypeSniffer;
use sha2::{Digest, Sha256};
#[derive(thiserror::Error, Debug)]
pub enum FileError {
#[error(transparent)]
WriteError(#[from] std::io::Error),
#[error("base64 decoding error")]
Base64DecodingError(#[from] base64::DecodeError),
#[error("invalid media type")]
InvalidMediaType,
}
pub fn save_file(data: Vec<u8>, output_dir: &PathBuf) -> Result<String, FileError> {
let digest = Sha256::digest(&data);
let mut file_name = hex::encode(digest);
let maybe_extension = data.sniff_mime_type()
.and_then(|media_type| get_mime_extensions_str(media_type))
.and_then(|extensions| extensions.first());
if let Some(extension) = maybe_extension {
// Append extension for known media types
file_name = format!("{}.{}", file_name, extension);
}
let file_path = output_dir.join(&file_name);
let mut file = File::create(&file_path)?;
file.write_all(&data)?;
Ok(file_name)
}
fn sniff_media_type(data: &Vec<u8>) -> Option<String> {
data.sniff_mime_type().map(|val| val.to_string())
}
pub fn save_b64_file(
b64data: &str,
output_dir: &PathBuf,
) -> Result<(String, Option<String>), FileError> {
let data = base64::decode(b64data)?;
let media_type = sniff_media_type(&data);
let file_name = save_file(data, output_dir)?;
Ok((file_name, media_type))
}
pub fn save_validated_b64_file(
b64data: &str,
output_dir: &PathBuf,
media_type_prefix: &str,
) -> Result<(String, String), FileError> {
let data = base64::decode(b64data)?;
let media_type = sniff_media_type(&data)
.ok_or(FileError::InvalidMediaType)?;
if !media_type.starts_with(media_type_prefix) {
return Err(FileError::InvalidMediaType);
}
let file_name = save_file(data, output_dir)?;
Ok((file_name, media_type))
}
pub fn get_file_url(instance_url: &str, file_name: &str) -> String {
format!("{}/media/{}", instance_url, file_name)
}