fix uploding bugs

This commit is contained in:
DioCrafts
2025-04-12 12:21:57 +02:00
parent bc235eb89a
commit 5306bdc9e3
8 changed files with 314 additions and 54 deletions
@@ -677,9 +677,14 @@ impl FileRepository for FileFsRepository {
match self.storage_mediator.get_folder_path(id).await {
Ok(path) => {
tracing::info!("Using folder path: {:?} for folder_id: {:?}", path, id);
// Convert to StoragePath
let path_str = path.to_string_lossy().to_string();
StoragePath::from_string(&path_str)
// Convert to StoragePath - use just the folder name to avoid path duplication
// Get just the folder name to avoid path duplication
let lossy = path.to_string_lossy().to_string();
let folder_name = path.file_name()
.and_then(|f| f.to_str())
.unwrap_or_else(|| &lossy);
tracing::info!("Using folder name: {} for StoragePath", folder_name);
StoragePath::from_string(folder_name)
},
Err(e) => {
tracing::error!("Error getting folder: {}", e);
@@ -863,13 +868,56 @@ impl FileRepository for FileFsRepository {
).await?;
// Ensure ID mapping is persisted - this is critical for later retrieval
let save_result = self.id_mapping_service.save_changes().await;
if let Err(e) = &save_result {
tracing::error!("Failed to save ID mapping for file {}: {}", id, e);
} else {
tracing::info!("Successfully saved ID mapping for file ID: {} -> path: {}", id, path_string);
// Ejecutar múltiples intentos de guardado con verificación para garantizar persistencia
for attempt in 1..=3 {
match self.id_mapping_service.save_changes().await {
Ok(_) => {
tracing::info!("Successfully saved ID mapping for file ID: {} -> path: {} (attempt {})", id, path_string, attempt);
// Verificar que el mapeo se puede recuperar después de guardado
if let Ok(verified_path) = self.id_mapping_service.get_path_by_id(&id).await {
if verified_path.to_string() == path_string {
tracing::info!("Verified ID mapping is retrievable after save: {} -> {}", id, path_string);
break; // Guaradado correcto y verificado, salir del bucle
} else {
tracing::error!("Mapping verification failed: expected {} but got {}", path_string, verified_path.to_string());
if attempt < 3 {
tracing::info!("Will retry saving ID mapping (attempt {}/3)", attempt + 1);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
continue;
} else {
return Err(FileRepositoryError::Other(
format!("Failed to verify ID mapping for file: {} after 3 attempts", id)
));
}
}
} else {
tracing::error!("Cannot verify mapping, ID {} not found after save", id);
if attempt < 3 {
tracing::info!("Will retry saving ID mapping (attempt {}/3)", attempt + 1);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
continue;
} else {
return Err(FileRepositoryError::Other(
format!("Failed to verify ID mapping for file: {} after 3 attempts", id)
));
}
}
},
Err(e) => {
tracing::error!("Failed to save ID mapping for file {}: {} (attempt {})", id, e, attempt);
if attempt < 3 {
tracing::info!("Will retry saving ID mapping (attempt {}/3)", attempt + 1);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
continue;
} else {
return Err(FileRepositoryError::Other(
format!("Failed to save ID mapping for file: {} after 3 attempts - {}", id, e)
));
}
}
}
}
save_result?;
// Invalidate any directory cache entries for the parent folders
// to ensure directory listings show the new file
@@ -896,9 +944,14 @@ impl FileRepository for FileFsRepository {
match self.storage_mediator.get_folder_path(fid).await {
Ok(path) => {
tracing::info!("Using folder path: {:?} for folder_id: {:?}", path, fid);
// Convert to StoragePath
let path_str = path.to_string_lossy().to_string();
StoragePath::from_string(&path_str)
// Convert to StoragePath - use just the folder name to avoid path duplication
// Get just the folder name to avoid path duplication
let lossy = path.to_string_lossy().to_string();
let folder_name = path.file_name()
.and_then(|f| f.to_str())
.unwrap_or_else(|| &lossy);
tracing::info!("Using folder name: {} for StoragePath", folder_name);
StoragePath::from_string(folder_name)
},
Err(e) => {
tracing::error!("Error getting folder: {}", e);
@@ -1143,8 +1196,14 @@ impl FileRepository for FileFsRepository {
match self.storage_mediator.get_folder_path(id).await {
Ok(path) => {
tracing::info!("Found folder with path: {:?}", path);
let path_str = path.to_string_lossy().to_string();
StoragePath::from_string(&path_str)
// Convert to StoragePath - use just the folder name to avoid path duplication
// Get just the folder name to avoid path duplication
let lossy = path.to_string_lossy().to_string();
let folder_name = path.file_name()
.and_then(|f| f.to_str())
.unwrap_or_else(|| &lossy);
tracing::info!("Using folder name: {} for StoragePath", folder_name);
StoragePath::from_string(folder_name)
},
Err(e) => {
tracing::error!("Error getting folder by ID: {}: {}", id, e);
@@ -1155,8 +1214,8 @@ impl FileRepository for FileFsRepository {
None => StoragePath::root(),
};
// Get the absolute folder path
let abs_folder_path = self.resolve_storage_path(&folder_storage_path);
// Get the absolute folder path without duplicate ./storage prefix
let abs_folder_path = self.path_service.resolve_path(&folder_storage_path);
tracing::info!("Absolute folder path: {:?}", abs_folder_path);
// Check if the directory exists
@@ -1475,8 +1534,14 @@ impl FileRepository for FileFsRepository {
Some(folder_id) => {
match self.storage_mediator.get_folder_path(folder_id).await {
Ok(path) => {
let path_str = path.to_string_lossy().to_string();
StoragePath::from_string(&path_str)
// Convert to StoragePath - use just the folder name to avoid path duplication
// Get just the folder name to avoid path duplication
let lossy = path.to_string_lossy().to_string();
let folder_name = path.file_name()
.and_then(|f| f.to_str())
.unwrap_or_else(|| &lossy);
tracing::info!("Target folder name: {} for StoragePath", folder_name);
StoragePath::from_string(folder_name)
},
Err(e) => {
return Err(FileRepositoryError::Other(
@@ -70,7 +70,7 @@ struct IdMap {
}
/// Constantes para configuración
const SAVE_DEBOUNCE_MS: u64 = 300; // Tiempo para agrupar operaciones de guardado
const SAVE_DEBOUNCE_MS: u64 = 0; // Sin debounce para garantizar guardado inmediato
/// Servicio para gestionar mapeos entre rutas y IDs únicos
pub struct IdMappingService {
@@ -389,7 +389,7 @@ impl IdMappingService {
}
}
/// Guarda cambios pendientes al disco
/// Guarda cambios pendientes al disco inmediatamente, sin debounce
pub async fn save_pending_changes(&self) -> Result<(), IdMappingError> {
// Verificar si hay cambios pendientes
{
@@ -399,20 +399,55 @@ impl IdMappingService {
}
}
// Implementar debounce para agrupación de guardados
let map_path = self.map_path.clone();
let self_clone = self.clone();
tokio::spawn(async move {
// Esperar un poco para permitir la agrupación de operaciones
time::sleep(Duration::from_millis(SAVE_DEBOUNCE_MS)).await;
if let Err(e) = self_clone.save_id_map().await {
tracing::error!("Failed to save ID map to {}: {}", map_path.display(), e);
// Guardar inmediatamente (sin debounce ni spawn)
match self.save_id_map().await {
Ok(_) => {
tracing::info!("ID mappings saved successfully to disk at {}", self.map_path.display());
// Verificar explícitamente que el archivo existe y tiene tamaño
match std::fs::metadata(&self.map_path) {
Ok(metadata) => {
if metadata.len() > 0 {
tracing::info!("Verified saved map file exists with size: {} bytes", metadata.len());
} else {
tracing::warn!("Map file exists but has zero size - this might cause issues");
}
},
Err(e) => {
tracing::error!("Failed to verify saved map file: {}", e);
// Intentar un segundo guardado si la verificación falla
if let Err(retry_err) = self.save_id_map().await {
tracing::error!("Second save attempt also failed: {}", retry_err);
return Err(IdMappingError::IoError(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Failed to verify and retry save: {}", retry_err)
)));
}
tracing::info!("Second save attempt succeeded");
}
}
Ok(())
},
Err(e) => {
tracing::error!("Failed to save ID map to {}: {}", self.map_path.display(), e);
// Intentar un segundo guardado con retraso en caso de error
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
match self.save_id_map().await {
Ok(_) => {
tracing::info!("Second save attempt succeeded after initial failure");
Ok(())
},
Err(retry_e) => {
tracing::error!("Second save attempt also failed: {}", retry_e);
Err(IdMappingError::IoError(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Failed to save ID mappings after retry: {}", retry_e)
)))
}
}
}
});
Ok(())
}
}
}
+36 -4
View File
@@ -122,9 +122,33 @@ impl FileHandler {
// Log additional debugging information
tracing::info!("Created file details: folder_id={:?}, size={}, path={}",
file.folder_id, file.size, file.path);
// VERIFICACIÓN ADICIONAL: Comprobar que el archivo es accesible inmediatamente después de subir
let file_id = file.id.clone(); // Clonar para uso en la verificación
match service.get_file(&file_id).await {
Ok(_) => tracing::info!("Verified file is immediately accessible after upload: {}", file_id),
Err(e) => {
tracing::warn!("File uploaded but not immediately accessible: {} - {}. This could cause issues in frontend.", file_id, e);
// Esperar un momento y comprobar de nuevo
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
if let Err(retry_e) = service.get_file(&file_id).await {
tracing::error!("File still not accessible after retry: {} - {}", file_id, retry_e);
} else {
tracing::info!("File became accessible after short delay: {}", file_id);
}
}
}
// Return success response with file information
(StatusCode::CREATED, Json(file)).into_response()
// Añadir cabecera para evitar caché del navegador en respuestas
let response = Response::builder()
.status(StatusCode::CREATED)
.header("Cache-Control", "no-cache, no-store, must-revalidate")
.header("Pragma", "no-cache")
.header("Expires", "0")
.body(axum::body::Body::from(serde_json::to_string(&file).unwrap()))
.unwrap();
response
},
Err(err) => {
tracing::error!("Error uploading file '{}' through service: {}", filename, err);
@@ -434,8 +458,16 @@ impl FileHandler {
tracing::info!("No files found in folder through service");
}
// Return the files as JSON response
(StatusCode::OK, Json(files)).into_response()
// Devolver respuesta con cabeceras para evitar caché del navegador
let response = Response::builder()
.status(StatusCode::OK)
.header("Cache-Control", "no-cache, no-store, must-revalidate")
.header("Pragma", "no-cache")
.header("Expires", "0")
.body(axum::body::Body::from(serde_json::to_string(&files).unwrap()))
.unwrap();
response
},
Err(err) => {
tracing::error!("Error listing files through service: {}", err);