Compare commits

..

2 Commits

Author SHA1 Message Date
409
5c09120c23 improve file streams 2025-09-06 18:00:58 +02:00
409
1c4aaa7040 improve viewer exports 2025-09-06 18:00:19 +02:00
7 changed files with 59 additions and 18 deletions

View File

@@ -116,11 +116,19 @@ pub enum FileType {
pub struct FileMimeType(String); pub struct FileMimeType(String);
impl FileMimeType { impl FileMimeType {
pub fn new_raw(raw: &str) -> Self {
Self(raw.to_owned())
}
pub fn from_name(name: &str) -> Option<Self> { pub fn from_name(name: &str) -> Option<Self> {
mime_guess::from_path(name) mime_guess::from_path(name)
.first_raw() .first_raw()
.map(|s| Self(s.to_owned())) .map(|s| Self(s.to_owned()))
} }
pub fn as_str(&self) -> &str {
self.0.as_str()
}
} }
/// A valid file path that might start with a slash /// A valid file path that might start with a slash
@@ -289,27 +297,39 @@ impl From<AbsoluteFilePath> for FilePath {
pub type FileStreamInner = pub type FileStreamInner =
Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send + Sync + Unpin + 'static>; Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send + Sync + Unpin + 'static>;
pub struct FileStream(FileType, FileStreamInner); pub struct FileStream {
file_type: FileType,
mime_type: Option<FileMimeType>,
stream: FileStreamInner,
}
impl FileStream { impl FileStream {
pub fn new<S>(file_type: FileType, stream: S) -> Self pub fn new<S>(file_type: FileType, mime_type: Option<FileMimeType>, stream: S) -> Self
where where
S: Stream<Item = Result<Bytes, std::io::Error>> + Send + Sync + Unpin + 'static, S: Stream<Item = Result<Bytes, std::io::Error>> + Send + Sync + Unpin + 'static,
{ {
Self(file_type, Box::new(stream)) Self {
file_type,
mime_type,
stream: Box::new(stream),
}
} }
pub fn file_type(&self) -> FileType { pub fn file_type(&self) -> FileType {
self.0 self.file_type
}
pub fn mime_type(&self) -> Option<&FileMimeType> {
self.mime_type.as_ref()
} }
pub fn stream(&self) -> &FileStreamInner { pub fn stream(&self) -> &FileStreamInner {
&self.1 &self.stream
} }
} }
impl From<FileStream> for FileStreamInner { impl From<FileStream> for FileStreamInner {
fn from(value: FileStream) -> Self { fn from(value: FileStream) -> Self {
value.1 value.stream
} }
} }

View File

@@ -76,18 +76,22 @@ impl IntoResponse for FileStream {
let mut builder = Response::builder().header(http::header::TRANSFER_ENCODING, "chunked"); let mut builder = Response::builder().header(http::header::TRANSFER_ENCODING, "chunked");
if let Some(headers) = builder.headers_mut() { if let Some(headers) = builder.headers_mut() {
if self.file_type() == FileType::Directory { if let Some(mime_type) = self.mime_type() {
headers.insert( headers.insert(
http::header::CONTENT_TYPE, http::header::CONTENT_TYPE,
HeaderValue::from_str("application/zip").unwrap(), HeaderValue::from_str(mime_type.as_str()).unwrap(),
); );
} }
headers.remove(http::header::CONTENT_LENGTH); headers.insert(
http::header::TRANSFER_ENCODING,
HeaderValue::from_str("chunked").unwrap(),
);
} }
let inner: FileStreamInner = self.into(); builder
builder.body(axum::body::Body::from_stream(inner)).unwrap() .body(axum::body::Body::from_stream(FileStreamInner::from(self)))
.unwrap()
} }
} }

View File

@@ -265,7 +265,11 @@ impl FileSystem {
} }
}); });
FileStream::new(FileType::Directory, ReceiverStream::new(rx)) FileStream::new(
FileType::Directory,
Some(FileMimeType::new_raw("application/zip")),
ReceiverStream::new(rx),
)
} }
match path_request { match path_request {
@@ -293,7 +297,20 @@ impl FileSystem {
bail!("File size exceeds configured limit"); bail!("File size exceeds configured limit");
} }
let stream = FileStream::new(FileType::File, ReaderStream::new(file)); let mime_type = {
let file_name = {
let path = file_path.as_str();
if let Some(last_slash_index) = path.rfind("/") {
&path[last_slash_index + 1..]
} else {
path
}
};
FileMimeType::from_name(file_name)
};
let stream = FileStream::new(FileType::File, mime_type, ReaderStream::new(file));
Ok(stream) Ok(stream)
} }

View File

@@ -38,7 +38,7 @@ export async function getWarrenDirectory(
warrenId, warrenId,
path, path,
}), }),
key: `${warrenId}-${path}`, key: `${warrenId}-${path}`,
}); });
if (data.value == null) { if (data.value == null) {

View File

@@ -1,4 +1,4 @@
export const useImageViewer = defineStore('image-viewer', { export default defineStore('image-viewer', {
state: () => ({ state: () => ({
src: null as string | null, src: null as string | null,
}), }),

View File

@@ -1,4 +1,4 @@
import { useImageViewer } from './image'; import useImageViewer from './image';
import { useTextEditor } from './text'; import useTextEditor from './text';
export { useImageViewer, useTextEditor }; export { useImageViewer, useTextEditor };

View File

@@ -1,7 +1,7 @@
import { uploadToWarren } from '~/lib/api/warrens'; import { uploadToWarren } from '~/lib/api/warrens';
import type { DirectoryEntry } from '~/shared/types'; import type { DirectoryEntry } from '~/shared/types';
export const useTextEditor = defineStore('text-editor', { export default defineStore('text-editor', {
state: () => ({ state: () => ({
data: null as { data: null as {
warrenId: string; warrenId: string;