ensure file ids are unique
This commit is contained in:
parent
63fff3adf3
commit
c520b3d469
|
@ -36,7 +36,7 @@ pub(crate) async fn delete_by_id(
|
||||||
file_id: &str,
|
file_id: &str,
|
||||||
files_dir: &Path,
|
files_dir: &Path,
|
||||||
) -> Result<(), sqlx::Error> {
|
) -> Result<(), sqlx::Error> {
|
||||||
delete_content(file_id, &files_dir).await?;
|
delete_content(file_id, files_dir).await?;
|
||||||
sqlx::query("DELETE FROM files WHERE file_id = $1")
|
sqlx::query("DELETE FROM files WHERE file_id = $1")
|
||||||
.bind(file_id)
|
.bind(file_id)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
|
|
|
@ -102,7 +102,7 @@ fn build_file_response(
|
||||||
} else {
|
} else {
|
||||||
DispositionType::Inline
|
DispositionType::Inline
|
||||||
},
|
},
|
||||||
parameters: get_disposition_params(&file_name),
|
parameters: get_disposition_params(file_name),
|
||||||
};
|
};
|
||||||
let file = NamedFile::open(path)
|
let file = NamedFile::open(path)
|
||||||
.map_err(|file_err| {
|
.map_err(|file_err| {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{config, file_kind::FileKind};
|
use crate::{config, file_kind::FileKind};
|
||||||
use actix_multipart::{Field, Multipart};
|
use actix_multipart::{Field, Multipart};
|
||||||
use actix_web::{error, http::header::DispositionParam};
|
use actix_web::{error, http::header::DispositionParam, Error};
|
||||||
use async_std::{fs, fs::File, path::Path, prelude::*};
|
use async_std::{fs::File, path::Path, prelude::*};
|
||||||
use chrono::{prelude::*, Duration};
|
use chrono::{prelude::*, Duration};
|
||||||
use futures::{StreamExt, TryStreamExt};
|
use futures::{StreamExt, TryStreamExt};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ pub(crate) struct UploadConfig {
|
||||||
pub(crate) async fn parse_multipart(
|
pub(crate) async fn parse_multipart(
|
||||||
mut payload: Multipart,
|
mut payload: Multipart,
|
||||||
file_id: &str,
|
file_id: &str,
|
||||||
filename: &Path,
|
file_name: &Path,
|
||||||
config: &config::Config,
|
config: &config::Config,
|
||||||
) -> Result<UploadConfig, error::Error> {
|
) -> Result<UploadConfig, error::Error> {
|
||||||
let mut original_name: Option<String> = None;
|
let mut original_name: Option<String> = None;
|
||||||
|
@ -42,11 +42,7 @@ pub(crate) async fn parse_multipart(
|
||||||
}
|
}
|
||||||
original_name = file_original_name;
|
original_name = file_original_name;
|
||||||
kind = Some(FileKind::Binary);
|
kind = Some(FileKind::Binary);
|
||||||
let mut file = fs::File::create(&filename).await.map_err(|file_err| {
|
size = create_file(file_name, field, config.max_file_size).await?;
|
||||||
log::error!("could not create file {:?}", file_err);
|
|
||||||
error::ErrorInternalServerError("could not create file")
|
|
||||||
})?;
|
|
||||||
size = write_to_file(&mut file, field, config.max_file_size).await?;
|
|
||||||
}
|
}
|
||||||
"text" => {
|
"text" => {
|
||||||
if original_name.is_some() {
|
if original_name.is_some() {
|
||||||
|
@ -54,11 +50,7 @@ pub(crate) async fn parse_multipart(
|
||||||
}
|
}
|
||||||
original_name = Some(format!("{}.txt", file_id));
|
original_name = Some(format!("{}.txt", file_id));
|
||||||
kind = Some(FileKind::Text);
|
kind = Some(FileKind::Text);
|
||||||
let mut file = fs::File::create(&filename).await.map_err(|file_err| {
|
size = create_file(file_name, field, config.max_file_size).await?;
|
||||||
log::error!("could not create file {:?}", file_err);
|
|
||||||
error::ErrorInternalServerError("could not create file")
|
|
||||||
})?;
|
|
||||||
size = write_to_file(&mut file, field, config.max_file_size).await?;
|
|
||||||
}
|
}
|
||||||
"delete_on_download" => {
|
"delete_on_download" => {
|
||||||
delete_on_download = dbg!(parse_string(name, field).await?) != "false";
|
delete_on_download = dbg!(parse_string(name, field).await?) != "false";
|
||||||
|
@ -145,9 +137,22 @@ async fn read_content(mut field: actix_multipart::Field) -> Result<Vec<u8>, erro
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_file(
|
||||||
|
filename: &Path,
|
||||||
|
field: Field,
|
||||||
|
max_file_size: Option<u64>,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
|
let mut file = File::create(&filename).await.map_err(|file_err| {
|
||||||
|
log::error!("could not create file {:?}", file_err);
|
||||||
|
error::ErrorInternalServerError("could not create file")
|
||||||
|
})?;
|
||||||
|
let written_bytes = write_to_file(&mut file, field, max_file_size).await?;
|
||||||
|
Ok(written_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
async fn write_to_file(
|
async fn write_to_file(
|
||||||
file: &mut File,
|
file: &mut File,
|
||||||
mut field: actix_multipart::Field,
|
mut field: Field,
|
||||||
max_size: Option<u64>,
|
max_size: Option<u64>,
|
||||||
) -> Result<u64, error::Error> {
|
) -> Result<u64, error::Error> {
|
||||||
let mut written_bytes: u64 = 0;
|
let mut written_bytes: u64 = 0;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::io::ErrorKind;
|
||||||
use std::{cmp, vec};
|
use std::{cmp, vec};
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
@ -6,7 +7,11 @@ use crate::multipart;
|
||||||
use crate::multipart::UploadConfig;
|
use crate::multipart::UploadConfig;
|
||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
use actix_web::{error, web, Error, HttpResponse};
|
use actix_web::{error, web, Error, HttpResponse};
|
||||||
use async_std::{channel::Sender, fs};
|
use async_std::{
|
||||||
|
channel::Sender,
|
||||||
|
fs::{self, OpenOptions},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use rand::prelude::SliceRandom;
|
use rand::prelude::SliceRandom;
|
||||||
use sqlx::postgres::PgPool;
|
use sqlx::postgres::PgPool;
|
||||||
|
@ -142,11 +147,12 @@ pub async fn upload(
|
||||||
expiry_watch_sender: web::Data<Sender<()>>,
|
expiry_watch_sender: web::Data<Sender<()>>,
|
||||||
config: web::Data<Config>,
|
config: web::Data<Config>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let file_id = gen_file_id();
|
let (file_id, file_name) = create_unique_file(&config).await.map_err(|file_err| {
|
||||||
let mut filename = config.files_dir.clone();
|
log::error!("could not create file {:?}", file_err);
|
||||||
filename.push(&file_id);
|
error::ErrorInternalServerError("could not create file")
|
||||||
|
})?;
|
||||||
|
|
||||||
let parsed_multipart = multipart::parse_multipart(payload, &file_id, &filename, &config).await;
|
let parsed_multipart = multipart::parse_multipart(payload, &file_id, &file_name, &config).await;
|
||||||
let UploadConfig {
|
let UploadConfig {
|
||||||
original_name,
|
original_name,
|
||||||
valid_till,
|
valid_till,
|
||||||
|
@ -155,8 +161,8 @@ pub async fn upload(
|
||||||
} = match parsed_multipart {
|
} = match parsed_multipart {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
if filename.exists().await {
|
if file_name.exists().await {
|
||||||
fs::remove_file(filename).await.map_err(|file_err| {
|
fs::remove_file(file_name).await.map_err(|file_err| {
|
||||||
log::error!("could not remove file {:?}", file_err);
|
log::error!("could not remove file {:?}", file_err);
|
||||||
error::ErrorInternalServerError(
|
error::ErrorInternalServerError(
|
||||||
"could not parse multipart; could not remove file",
|
"could not parse multipart; could not remove file",
|
||||||
|
@ -180,7 +186,7 @@ pub async fn upload(
|
||||||
.await;
|
.await;
|
||||||
if let Err(db_err) = db_insert {
|
if let Err(db_err) = db_insert {
|
||||||
log::error!("could not insert into datebase {:?}", db_err);
|
log::error!("could not insert into datebase {:?}", db_err);
|
||||||
fs::remove_file(filename).await.map_err(|file_err| {
|
fs::remove_file(file_name).await.map_err(|file_err| {
|
||||||
log::error!("could not remove file {:?}", file_err);
|
log::error!("could not remove file {:?}", file_err);
|
||||||
error::ErrorInternalServerError(
|
error::ErrorInternalServerError(
|
||||||
"could not insert file into database; could not remove file",
|
"could not insert file into database; could not remove file",
|
||||||
|
@ -215,6 +221,26 @@ pub async fn upload(
|
||||||
.body(format!("{}\n", url)))
|
.body(format!("{}\n", url)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn create_unique_file(
|
||||||
|
config: &web::Data<Config>,
|
||||||
|
) -> Result<(String, PathBuf), std::io::Error> {
|
||||||
|
loop {
|
||||||
|
let file_id = gen_file_id();
|
||||||
|
let mut file_name = config.files_dir.clone();
|
||||||
|
file_name.push(&file_id);
|
||||||
|
match OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create_new(true)
|
||||||
|
.open(&file_name)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => return Ok((file_id, file_name)),
|
||||||
|
Err(error) if error.kind() == ErrorKind::AlreadyExists => continue,
|
||||||
|
Err(error) => return Err(error),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_file_id() -> String {
|
fn gen_file_id() -> String {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut id = String::with_capacity(5);
|
let mut id = String::with_capacity(5);
|
||||||
|
|
Loading…
Reference in New Issue