mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: initial file upload api (#4299)
* feat: initial file upload api * feat: initial file upload api * fix: add pb index * feat: remove file name * feat: read everything to mem * feat: revamp object storage * chore: cargo format * chore: update deps * feat: revised implementations and style * chore: use deploy env instead * chore: use deploy env instead * chore: use deploy env instead * refactor: move logic to handler to manager * fix: format issues * fix: cargo clippy * chore: cargo check tauri * fix: debug docker integration test * fix: debug docker integration test * fix: debug docker integration test gotrue * fix: debug docker integration test docker compose version * fix: docker scripts * fix: cargo fmt * fix: add sleep after docker compose up --------- Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
@ -15,3 +15,6 @@ mime_guess = "2.0"
|
||||
lib-infra = { workspace = true }
|
||||
url = "2.2.2"
|
||||
flowy-error = { workspace = true, features = ["impl_from_reqwest"] }
|
||||
mime = "0.3.17"
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
|
@ -2,11 +2,108 @@ use bytes::Bytes;
|
||||
|
||||
use flowy_error::FlowyError;
|
||||
use lib_infra::future::FutureResult;
|
||||
use mime::Mime;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tracing::info;
|
||||
|
||||
pub struct ObjectIdentity {
|
||||
pub workspace_id: String,
|
||||
pub file_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ObjectValue {
|
||||
pub raw: Bytes,
|
||||
pub mime: Mime,
|
||||
}
|
||||
|
||||
impl ObjectValue {
|
||||
pub async fn from_file(local_file_path: &str) -> Result<Self, FlowyError> {
|
||||
let mut file = tokio::fs::File::open(local_file_path).await?;
|
||||
let mut content = Vec::new();
|
||||
let n = file.read_to_end(&mut content).await?;
|
||||
info!("read {} bytes from file: {}", n, local_file_path);
|
||||
let mime = mime_guess::from_path(local_file_path).first_or_octet_stream();
|
||||
|
||||
Ok(ObjectValue {
|
||||
raw: content.into(),
|
||||
mime,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a service for object storage.
|
||||
///
|
||||
/// The trait includes methods for CRUD operations on storage objects.
|
||||
pub trait ObjectStorageService: Send + Sync + 'static {
|
||||
/// Creates a new storage object.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `url`: url of the object to be created.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok()`
|
||||
/// - `Err(Error)`: An error occurred during the operation.
|
||||
fn get_object_url(&self, object_id: ObjectIdentity) -> FutureResult<String, FlowyError>;
|
||||
|
||||
/// Creates a new storage object.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `url`: url of the object to be created.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok()`
|
||||
/// - `Err(Error)`: An error occurred during the operation.
|
||||
fn put_object(&self, url: String, object_value: ObjectValue) -> FutureResult<(), FlowyError>;
|
||||
|
||||
/// Deletes a storage object by its URL.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `url`: url of the object to be deleted.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok()`
|
||||
/// - `Err(Error)`: An error occurred during the operation.
|
||||
fn delete_object(&self, url: String) -> FutureResult<(), FlowyError>;
|
||||
|
||||
/// Fetches a storage object by its URL.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `url`: url of the object
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(File)`: The returned file object.
|
||||
/// - `Err(Error)`: An error occurred during the operation.
|
||||
fn get_object(&self, url: String) -> FutureResult<ObjectValue, FlowyError>;
|
||||
}
|
||||
|
||||
pub trait FileStoragePlan: Send + Sync + 'static {
|
||||
fn storage_size(&self) -> FutureResult<u64, FlowyError>;
|
||||
fn maximum_file_size(&self) -> FutureResult<u64, FlowyError>;
|
||||
|
||||
fn check_upload_object(&self, object: &StorageObject) -> FutureResult<(), FlowyError>;
|
||||
}
|
||||
|
||||
pub struct StorageObject {
|
||||
pub workspace_id: String,
|
||||
pub file_name: String,
|
||||
pub value: ObjectValue,
|
||||
pub value: ObjectValueSupabase,
|
||||
}
|
||||
|
||||
pub enum ObjectValueSupabase {
|
||||
File { file_path: String },
|
||||
Bytes { bytes: Bytes, mime: String },
|
||||
}
|
||||
|
||||
impl ObjectValueSupabase {
|
||||
pub fn mime_type(&self) -> String {
|
||||
match self {
|
||||
ObjectValueSupabase::File { file_path } => mime_guess::from_path(file_path)
|
||||
.first_or_octet_stream()
|
||||
.to_string(),
|
||||
ObjectValueSupabase::Bytes { mime, .. } => mime.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StorageObject {
|
||||
@ -21,7 +118,7 @@ impl StorageObject {
|
||||
Self {
|
||||
workspace_id: workspace_id.to_string(),
|
||||
file_name: file_name.to_string(),
|
||||
value: ObjectValue::File {
|
||||
value: ObjectValueSupabase::File {
|
||||
file_path: file_path.to_string(),
|
||||
},
|
||||
}
|
||||
@ -45,7 +142,7 @@ impl StorageObject {
|
||||
Self {
|
||||
workspace_id: workspace_id.to_string(),
|
||||
file_name: file_name.to_string(),
|
||||
value: ObjectValue::Bytes { bytes, mime },
|
||||
value: ObjectValueSupabase::Bytes { bytes, mime },
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,60 +153,8 @@ impl StorageObject {
|
||||
/// The file size in bytes.
|
||||
pub fn file_size(&self) -> u64 {
|
||||
match &self.value {
|
||||
ObjectValue::File { file_path } => std::fs::metadata(file_path).unwrap().len(),
|
||||
ObjectValue::Bytes { bytes, .. } => bytes.len() as u64,
|
||||
ObjectValueSupabase::File { file_path } => std::fs::metadata(file_path).unwrap().len(),
|
||||
ObjectValueSupabase::Bytes { bytes, .. } => bytes.len() as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ObjectValue {
|
||||
File { file_path: String },
|
||||
Bytes { bytes: Bytes, mime: String },
|
||||
}
|
||||
|
||||
impl ObjectValue {
|
||||
pub fn mime_type(&self) -> String {
|
||||
match self {
|
||||
ObjectValue::File { file_path } => mime_guess::from_path(file_path)
|
||||
.first_or_octet_stream()
|
||||
.to_string(),
|
||||
ObjectValue::Bytes { mime, .. } => mime.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides a service for storing and managing files.
|
||||
///
|
||||
/// The trait includes methods for CRUD operations on storage objects.
|
||||
pub trait FileStorageService: Send + Sync + 'static {
|
||||
/// Creates a new storage object.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `object`: The object to be stored.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Ok(String)`: A url representing some kind of object identifier.
|
||||
/// - `Err(Error)`: An error occurred during the operation.
|
||||
fn create_object(&self, object: StorageObject) -> FutureResult<String, FlowyError>;
|
||||
|
||||
/// Deletes a storage object by its URL.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `object_url`: The URL of the object to be deleted.
|
||||
///
|
||||
fn delete_object_by_url(&self, object_url: String) -> FutureResult<(), FlowyError>;
|
||||
|
||||
/// Fetches a storage object by its URL.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `object_url`: The URL of the object to be fetched.
|
||||
///
|
||||
fn get_object_by_url(&self, object_url: String) -> FutureResult<Bytes, FlowyError>;
|
||||
}
|
||||
|
||||
pub trait FileStoragePlan: Send + Sync + 'static {
|
||||
fn storage_size(&self) -> FutureResult<u64, FlowyError>;
|
||||
fn maximum_file_size(&self) -> FutureResult<u64, FlowyError>;
|
||||
|
||||
fn check_upload_object(&self, object: &StorageObject) -> FutureResult<(), FlowyError>;
|
||||
}
|
||||
|
Reference in New Issue
Block a user