get images, and display images

This commit is contained in:
m
2026-07-11 22:59:25 +02:00
parent 1cc8b94b60
commit f7cf2a2bef
5 changed files with 164 additions and 4 deletions
+1
View File
@@ -19,6 +19,7 @@ func main() {
r.GET("/api/v1/health", handlers.Health)
r.GET("/api/v1/files", handlers.ListFiles)
r.GET("/api/v1/file/:id", handlers.ListFile)
r.POST("/api/v1/files/upload", handlers.UploadFile)
r.GET("/api/v1/files/:id", handlers.GetFile)
r.DELETE("/api/v1/files/:id", handlers.DeleteFile)
+66 -1
View File
@@ -70,6 +70,71 @@ func ListFiles(c *gin.Context) {
})
}
func ListFile(c *gin.Context) {
id := c.Query("id")
dirs, err := os.ReadDir("./uploads/")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"data": []interface{}{},
"meta": gin.H{
"page": 1,
"total": 0,
},
})
return
}
type Finfo struct {
Url string `json:"url"`
Name string `json:"name"`
Size int64 `json:"size"`
Tags []string `json:"tags"`
}
file := Finfo{}
for _, dir := range dirs {
if dir.IsDir() {
continue
}
i, e := dir.Info()
if e != nil {
continue
}
if id != i.Name() {
continue
}
ps := Finfo{
Url: service.GenerateFileDownloadUrl(i.Name()),
Name: i.Name(),
Size: i.Size(),
Tags: []string{},
}
file = ps
break
}
c.JSON(http.StatusOK, gin.H{
"data": file,
"meta": gin.H{
"total": 1,
},
})
}
func GetFile(c *gin.Context) {
exp, _ := strconv.ParseInt(c.Query("expires"), 10, 64)
@@ -78,7 +143,7 @@ func GetFile(c *gin.Context) {
if r != true {
c.JSON(http.StatusNotFound, gin.H{
c.JSON(http.StatusForbidden, gin.H{
"error": gin.H{
"code": "FILE_NOT_FOUND",
"message": "File not found",
+32 -3
View File
@@ -1,7 +1,8 @@
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { View, Text, Image, StyleSheet, TouchableOpacity, ActivityIndicator } from 'react-native';
import { FileItem } from '../types';
import { TagChip } from './TagChip';
import { useFileImage } from '../hooks/useFileImage';
interface FileCardProps {
file: FileItem;
@@ -9,16 +10,25 @@ interface FileCardProps {
}
export function FileCard({ file, onPress }: FileCardProps) {
const { localUri, loading } = useFileImage(file.url);
console.log(localUri)
const formatSize = (bytes: number) => {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
};
console.log(file)
return (
<TouchableOpacity style={styles.container} onPress={() => onPress?.(file)}>
{file.url && (
<View style={styles.imageContainer}>
{loading && <ActivityIndicator style={styles.imageLoader} />}
{localUri && (
<Image source={{ uri: localUri }} style={styles.image} resizeMode="cover" />
)}
</View>
)}
<View style={styles.header}>
<Text style={styles.name} numberOfLines={1}>
{file.name}
@@ -53,6 +63,25 @@ const styles = StyleSheet.create({
shadowRadius: 2,
elevation: 2,
},
imageContainer: {
width: '100%',
height: 200,
borderRadius: 8,
overflow: 'hidden',
marginBottom: 12,
backgroundColor: '#f0f0f0',
},
image: {
width: '100%',
height: '100%',
},
imageLoader: {
position: 'absolute',
top: '50%',
left: '50%',
marginTop: -10,
marginLeft: -10,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
+64
View File
@@ -0,0 +1,64 @@
import { useEffect, useState } from 'react';
import { downloadAsync, documentDirectory, makeDirectoryAsync, getInfoAsync } from 'expo-file-system/legacy';
const CACHE_DIR = `${documentDirectory}file-images/`;
function getCacheKey(url: string): string {
let hash = 0;
for (let i = 0; i < url.length; i++) {
hash = ((hash << 5) - hash + url.charCodeAt(i)) | 0;
}
return Math.abs(hash).toString(36);
}
function getExtension(url: string): string {
const pathname = new URL(url).pathname;
const dot = pathname.lastIndexOf('.');
return dot >= 0 ? pathname.slice(dot) : '.jpg';
}
export function useFileImage(url: string | undefined) {
const [localUri, setLocalUri] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!url) return;
const resolvedUrl = url;
let cancelled = false;
async function load() {
const fileName = `${getCacheKey(resolvedUrl)}${getExtension(resolvedUrl)}`;
const fileUri = `${CACHE_DIR}${fileName}`;
const info = await getInfoAsync(fileUri);
if (info.exists) {
if (!cancelled) setLocalUri(info.uri);
return;
}
setLoading(true);
try {
await makeDirectoryAsync(CACHE_DIR, { intermediates: true });
const result = await downloadAsync(resolvedUrl, fileUri);
if (!cancelled) {
setLocalUri(result.uri);
}
} catch {
if (!cancelled) {
setLocalUri(null);
}
} finally {
if (!cancelled) setLoading(false);
}
}
load();
return () => {
cancelled = true;
};
}, [url]);
return { localUri, loading };
}
+1
View File
@@ -7,6 +7,7 @@ export interface FileItem {
updatedAt: string;
ocrText?: string;
tags: Tag[];
url?: string;
}
export interface Tag {