diff --git a/assets/index.html b/assets/index.html
index 2a43b8a..6168052 100644
--- a/assets/index.html
+++ b/assets/index.html
@@ -16,7 +16,7 @@
-
+
diff --git a/assets/js/app.js b/assets/js/app.js
index 2358298..5610c8b 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -53,8 +53,8 @@ function initSearch() {
// 初始化无限滚动
function initInfiniteScroll() {
let scrollTimeout;
-
- window.addEventListener('scroll', function() {
+
+ window.addEventListener('scroll', function () {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(handleScroll, 100);
});
@@ -63,12 +63,12 @@ function initInfiniteScroll() {
// 处理滚动事件
function handleScroll() {
if (isLoadingMore || !hasMoreAlbums) return;
-
+
// 检查是否滚动到底部
const scrollTop = window.scrollY || document.documentElement.scrollTop;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = window.innerHeight;
-
+
// 当距离底部还有200px时开始加载
if (scrollTop + clientHeight >= scrollHeight - 200) {
loadMoreAlbums();
@@ -78,7 +78,7 @@ function handleScroll() {
// 加载更多相册
async function loadMoreAlbums() {
if (isLoadingMore || !hasMoreAlbums) return;
-
+
isLoadingMore = true;
try {
await loadAlbums();
@@ -101,18 +101,13 @@ function showLoadMoreError() {
加载失败,点击重试
`;
-
+
const existingError = container.querySelector('.load-more-error');
if (existingError) {
existingError.remove();
}
-
- container.appendChild(errorElement);
-}
-// 防抖搜索
-function debouncedSearch() {
- searchAlbums();
+ container.appendChild(errorElement);
}
// 显示骨架屏
@@ -196,7 +191,7 @@ function appendAlbums(albums) {
`;
}).join('');
-
+
// 移除加载指示器并添加新相册
hideLoadingMore();
container.insertAdjacentHTML('beforeend', newAlbumsHTML);
@@ -222,37 +217,37 @@ async function loadAlbums(reset = false) {
showLoadingMore();
}
- const response = await fetch(`${API_BASE}/albums?page=${currentPage}&limit=${albumsPerPage}`);
+ const response = await fetch(`${API_BASE}/albums`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
-
+
const newAlbums = await response.json();
-
+
// 检查是否还有更多数据
if (newAlbums.length < albumsPerPage) {
hasMoreAlbums = false;
}
-
+
// 合并到现有相册列表
allAlbums = [...allAlbums, ...newAlbums];
-
+
if (currentPage === 1) {
displayAlbums(allAlbums);
} else {
appendAlbums(newAlbums);
}
-
+
updateStats(allAlbums);
// 初始化图片懒加载
initLazyLoading();
-
+
// 预加载下一批图片
preloadNextBatchImages();
-
+
currentPage++;
-
+
// 隐藏加载更多指示器
hideLoadingMore();
} catch (error) {
@@ -264,7 +259,7 @@ async function loadAlbums(reset = false) {
// 搜索相册
// 防抖搜索函数
-function debouncedSearch() {
+async function debouncedSearch() {
const searchTerm = document.getElementById('search-input').value.toLowerCase().trim();
if (searchTerm === '') {
@@ -274,28 +269,19 @@ function debouncedSearch() {
return;
}
- // 在当前已加载的数据集中搜索
- const filteredAlbums = searchInLoadedAlbums(searchTerm);
-
- if (filteredAlbums.length > 0) {
- // 如果当前已加载的数据中有匹配项,直接显示
- displayAlbums(filteredAlbums);
- document.getElementById('stats').innerHTML = `在当前页面中找到 ${filteredAlbums.length} 个包含 "${searchTerm}" 的相册`;
- } else {
- // 如果没有找到匹配项,显示提示信息
- showSearchHint(searchTerm);
- }
+ // 直接使用后端搜索API进行全站搜索
+ await performServerSearch(searchTerm);
}
// 在当前已加载的数据集中搜索
function searchInLoadedAlbums(searchTerm) {
if (!allAlbums.length) return [];
-
+
return allAlbums.filter(album => {
// 搜索标题和描述
const titleMatch = album.title.toLowerCase().includes(searchTerm);
const descriptionMatch = album.description && album.description.toLowerCase().includes(searchTerm);
-
+
return titleMatch || descriptionMatch;
});
}
@@ -328,21 +314,21 @@ async function performServerSearch(searchTerm) {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
-
+
const serverResults = await response.json();
-
+
if (serverResults.length === 0) {
showError(`没有找到包含 "${searchTerm}" 的相册`);
return;
}
-
+
// 显示服务器搜索结果
displayAlbums(serverResults);
document.getElementById('stats').innerHTML = `在全站找到 ${serverResults.length} 个包含 "${searchTerm}" 的相册`;
-
+
// 初始化图片懒加载
initLazyLoading();
-
+
} catch (error) {
console.error('全站搜索失败:', error);
showError('全站搜索失败,请重试');
@@ -416,7 +402,7 @@ function initLazyLoading() {
threshold: 0.01
});
}
-
+
const lazyImages = document.querySelectorAll('img.lazy');
lazyImages.forEach(img => {
lazyLoadObserver.observe(img);
@@ -427,14 +413,14 @@ function initLazyLoading() {
function preloadNextBatchImages() {
const imagesToPreload = [];
const visibleImages = document.querySelectorAll('img.lazy');
-
+
// 预加载前10张可见图片
visibleImages.forEach((img, index) => {
if (index < 10 && img.dataset.src) {
imagesToPreload.push(img.dataset.src);
}
});
-
+
// 使用link预加载
imagesToPreload.forEach(src => {
const link = document.createElement('link');
@@ -461,20 +447,24 @@ function showError(message) {
document.getElementById('stats').innerHTML = '';
}
-// 键盘事件处理
-function handleKeyPress(event) {
- if (event.key === 'Enter') {
- searchAlbums();
- }
+// 搜索相册函数
+async function searchAlbums() {
+ await debouncedSearch();
}
+
// 性能监控
function logPerformance() {
if ('performance' in window) {
window.addEventListener('load', () => {
setTimeout(() => {
const navigationTiming = performance.getEntriesByType('navigation')[0];
- console.log('页面加载时间:', navigationTiming.loadEventEnd - navigationTiming.navigationStart, 'ms');
+ if (navigationTiming && navigationTiming.loadEventEnd && navigationTiming.navigationStart) {
+ const loadTime = navigationTiming.loadEventEnd - navigationTiming.navigationStart;
+ console.log('页面加载时间:', loadTime, 'ms');
+ } else {
+ console.log('页面加载时间: 无法计算');
+ }
}, 0);
});
}
@@ -502,7 +492,7 @@ function preloadCriticalResources() {
'/assets/css/style.css',
'/assets/webfonts/fa-solid-900.woff2'
];
-
+
criticalResources.forEach(resource => {
const link = document.createElement('link');
link.rel = 'preload';
@@ -511,13 +501,13 @@ function preloadCriticalResources() {
link.crossOrigin = 'anonymous';
document.head.appendChild(link);
});
-
+
// 预加载第一屏可能需要的图片
setTimeout(() => {
const firstScreenImages = [
'/assets/placeholder.svg'
];
-
+
firstScreenImages.forEach(img => {
const preload = document.createElement('link');
preload.rel = 'preload';
@@ -532,7 +522,7 @@ function preloadCriticalResources() {
function optimizeScrollPerformance() {
// 使用requestAnimationFrame优化滚动
let ticking = false;
-
+
function updateOnScroll() {
// 在滚动时暂停非关键任务
if (!ticking) {
@@ -544,8 +534,8 @@ function optimizeScrollPerformance() {
ticking = true;
}
}
-
- window.addEventListener('scroll', updateOnScroll, { passive: true });
+
+ window.addEventListener('scroll', updateOnScroll, {passive: true});
}
// 延迟加载非关键图片
@@ -589,7 +579,7 @@ function openRandomAlbum() {
const randomButton = document.querySelector('.random-button');
randomButton.style.transform = 'scale(0.95)';
randomButton.disabled = true;
-
+
// 显示加载状态
const originalText = randomButton.textContent;
randomButton.textContent = '随机加载中...';
@@ -623,7 +613,7 @@ function openRandomAlbum() {
randomButton.disabled = false;
randomButton.textContent = originalText;
}, 200);
-
+
alert(error.message);
});
}
@@ -771,7 +761,7 @@ function previewFavorite(index) {
// 显示预览模态框
function showPreviewModal(index) {
const favorite = currentFavorites[index];
-
+
// 构建模态框HTML
const modalHtml = `
@@ -830,16 +820,16 @@ function showPreviewModal(index) {
`;
-
+
// 添加到页面
document.body.insertAdjacentHTML('beforeend', modalHtml);
-
+
// 显示模态框
setTimeout(() => {
const modal = document.getElementById('previewModal');
modal.classList.add('active');
}, 10);
-
+
// 监听键盘事件
document.addEventListener('keydown', handlePreviewKeydown);
}
@@ -853,7 +843,7 @@ function closePreviewModal() {
modal.remove();
}, 300);
}
-
+
// 移除键盘事件监听
document.removeEventListener('keydown', handlePreviewKeydown);
}
@@ -870,30 +860,30 @@ function navigatePreview(direction) {
function updatePreviewContent(newIndex) {
const favorite = currentFavorites[newIndex];
currentPreviewIndex = newIndex;
-
+
// 更新标题和相册信息
const titleElement = document.querySelector('.preview-title h3');
const infoElement = document.querySelector('.preview-info');
if (titleElement) titleElement.textContent = favorite.image.title || '未命名图片';
if (infoElement) infoElement.textContent = `来自: ${favorite.album_title}`;
-
+
// 更新计数器
const counterElement = document.querySelector('.preview-counter');
if (counterElement) counterElement.textContent = `${newIndex + 1} / ${currentFavorites.length}`;
-
+
// 更新导航按钮状态
const prevButton = document.querySelector('.preview-prev');
const nextButton = document.querySelector('.preview-next');
if (prevButton) prevButton.disabled = newIndex === 0;
if (nextButton) nextButton.disabled = newIndex === currentFavorites.length - 1;
-
+
// 更新元数据
const metaElements = document.querySelectorAll('.preview-meta .meta-item');
if (metaElements.length >= 3) {
metaElements[0].innerHTML = ` ${formatDate(favorite.favorite.created_at)}`;
metaElements[1].innerHTML = ` ${favorite.image.width} × ${favorite.image.height}`;
}
-
+
// 更新操作按钮
const openAlbumButton = document.querySelector('.preview-action:first-child');
const removeButton = document.querySelector('.preview-remove');
@@ -903,22 +893,22 @@ function updatePreviewContent(newIndex) {
if (removeButton) {
removeButton.onclick = () => removeFavoriteFromPreview(favorite.favorite.id);
}
-
+
// 更新图片
const imageElement = document.getElementById('preview-image');
-
- if (imageElement ) {
+
+ if (imageElement) {
// 显示加载状态
imageElement.style.opacity = '0';
-
+
// 加载新图片
const newImage = new Image();
newImage.src = `${API_BASE}/original/${favorite.image.id}`;
newImage.alt = favorite.image.title || '预览图片';
newImage.className = 'preview-image';
-
- newImage.onload = function() {
+
+ newImage.onload = function () {
// 替换图片
if (imageElement.parentNode) {
imageElement.parentNode.replaceChild(newImage, imageElement);
@@ -928,8 +918,8 @@ function updatePreviewContent(newIndex) {
newImage.style.opacity = '1';
};
-
- newImage.onerror = function() {
+
+ newImage.onerror = function () {
// 图片加载失败
imageElement.innerHTML = '图片加载失败';
};
@@ -1010,7 +1000,6 @@ function openFavoriteImage(favoriteId) {
}
// 导出函数供全局使用
-window.handleKeyPress = handleKeyPress;
window.searchAlbums = searchAlbums;
window.openRandomAlbum = openRandomAlbum;
window.showFavorites = showFavorites;
\ No newline at end of file
diff --git a/src/image_processor.rs b/src/image_processor.rs
index b9bddd2..f497908 100644
--- a/src/image_processor.rs
+++ b/src/image_processor.rs
@@ -85,8 +85,8 @@ impl ImageProcessor {
// 使用快速解码和缩放
let img = image::load_from_memory(image_data)?;
- // 快速缩放到小尺寸(200x150)
- let thumbnail = img.thumbnail(200, 150);
+ // 快速缩放到小尺寸(200x200)
+ let thumbnail = img.thumbnail(200, 200);
// 使用低质量JPEG压缩以加快编码速度
let mut output = Vec::new();
@@ -165,8 +165,8 @@ impl ImageProcessor {
// 加载图片
let img = image::load_from_memory(image_data)?;
- // 生成缩略图(400x300,保持宽高比)
- let thumbnail = img.thumbnail(600, 400);
+ // 生成缩略图(600x600,保持宽高比)
+ let thumbnail = img.thumbnail(600, 600);
// 将缩略图数据保存到内存缓冲区
let mut buffer = std::io::Cursor::new(Vec::new());
diff --git a/src/import.rs b/src/import.rs
index 29107c5..e17687a 100644
--- a/src/import.rs
+++ b/src/import.rs
@@ -9,6 +9,51 @@ use crate::config::Config;
use crate::database::Database;
use crate::models::*;
+/// 批量插入图片并设置封面缩略图的辅助函数
+async fn batch_insert_images_and_set_cover(
+ db: &Database,
+ images_to_insert: Vec,
+ album_id: Uuid,
+ album_path: &Path,
+ folder_name: &str,
+) -> Result> {
+ if images_to_insert.is_empty() {
+ return Ok(0);
+ }
+
+ println!(" 批量插入 {} 张图片...", images_to_insert.len());
+ match db.add_images(images_to_insert).await {
+ Ok(ids) => {
+ println!(" 批量插入成功,共插入 {} 张图片", ids.len());
+
+ // 设置第一张图片为封面图片并生成缩略图
+ if let Some(first_image_id) = ids.first() {
+ println!(" 设置第一张图片为封面图片 (ID: {})...", first_image_id);
+
+ // 更新相册的封面图片ID
+ if let Err(e) = db.update_album_cover(album_id, Some(*first_image_id)).await {
+ eprintln!(" 更新相册封面失败: {}", e);
+ }
+
+ // 获取第一张图片的信息
+ if let Ok(Some(first_image)) = db.get_image(*first_image_id).await {
+ // 生成缩略图
+ match generate_thumbnail_for_image(&first_image, album_path, folder_name).await {
+ Ok(_) => println!(" 封面缩略图生成成功"),
+ Err(e) => eprintln!(" 封面缩略图生成失败: {}", e),
+ }
+ }
+ }
+
+ Ok(ids.len())
+ }
+ Err(e) => {
+ eprintln!(" 批量插入失败: {}", e);
+ Ok(0)
+ }
+ }
+}
+
#[derive(Debug, Deserialize)]
struct AlbumIndex {
title: String,
@@ -125,9 +170,6 @@ pub async fn import_existing_data(config: &Config, db: Arc) -> Result<
// 继续处理下一个相册,不中断整个导入过程
}
}
-
- // 在每个相册导入后添加短暂延迟,减少数据库压力
- tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
println!("🎉 导入完成!成功导入 {} 个相册,共 {} 张图片", successful_albums, total_imported);
@@ -261,42 +303,8 @@ async fn import_single_album(db: &Database, folder_name: &str, path: &Path) -> R
}
}
- // 批量插入图片
- if !images_to_insert.is_empty() {
- println!(" 批量插入 {} 张图片...", images_to_insert.len());
- match db.add_images(images_to_insert).await {
- Ok(ids) => {
- println!(" 批量插入成功,共插入 {} 张图片", ids.len());
-
- // 设置第一张图片为封面图片并生成缩略图
- if let Some(first_image_id) = ids.first() {
- println!(" 设置第一张图片为封面图片 (ID: {})...", first_image_id);
-
- // 更新相册的封面图片ID
- if let Err(e) = db.update_album_cover(album_id, Some(*first_image_id)).await {
- eprintln!(" 更新相册封面失败: {}", e);
- }
-
- // 获取第一张图片的信息
- if let Ok(Some(first_image)) = db.get_image(*first_image_id).await {
- // 生成缩略图
- match generate_thumbnail_for_image(&first_image, path, folder_name).await {
- Ok(_) => println!(" 封面缩略图生成成功"),
- Err(e) => eprintln!(" 封面缩略图生成失败: {}", e),
- }
- }
- }
-
- Ok(ids.len())
- }
- Err(e) => {
- eprintln!(" 批量插入失败: {}", e);
- Ok(0)
- }
- }
- } else {
- Ok(0)
- }
+ // 使用辅助函数批量插入图片并设置封面
+ batch_insert_images_and_set_cover(&db, images_to_insert, album_id, path, folder_name).await
}
/// 快速扫描图片文件
@@ -438,47 +446,13 @@ async fn reimport_single_album(
println!(" 发现 {} 张图片需要导入", images_to_insert.len());
- // 批量插入图片
- if !images_to_insert.is_empty() {
- println!(" 批量插入 {} 张图片...", images_to_insert.len());
- match db.add_images(images_to_insert).await {
- Ok(ids) => {
- println!(" 批量插入成功,共插入 {} 张图片", ids.len());
-
- // 设置第一张图片为封面图片并生成缩略图
- if let Some(first_image_id) = ids.first() {
- println!(" 设置第一张图片为封面图片 (ID: {})...", first_image_id);
-
- // 更新相册的封面图片ID
- if let Err(e) = db.update_album_cover(album_id, Some(*first_image_id)).await {
- eprintln!(" 更新相册封面失败: {}", e);
- }
-
- // 获取第一张图片的信息
- if let Ok(Some(first_image)) = db.get_image(*first_image_id).await {
- // 生成缩略图
- match generate_thumbnail_for_image(&first_image, path, folder_name).await {
- Ok(_) => println!(" 封面缩略图生成成功"),
- Err(e) => eprintln!(" 封面缩略图生成失败: {}", e),
- }
- }
- }
-
- Ok(ids.len())
- }
- Err(e) => {
- eprintln!(" 批量插入失败: {}", e);
- Ok(0)
- }
- }
- } else {
- Ok(0)
- }
+ // 使用辅助函数批量插入图片并设置封面
+ batch_insert_images_and_set_cover(&db, images_to_insert, album_id, path, folder_name).await
}
/// 为图片生成缩略图(使用ImageProcessor)
async fn generate_thumbnail_for_image(
- image: &crate::models::Image,
+ image: &Image,
album_path: &Path,
_folder_name: &str,
) -> Result<(), Box> {
diff --git a/src/main.rs b/src/main.rs
index 5323896..cc7203c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -54,7 +54,7 @@ async fn main() -> std::io::Result<()> {
// 初始化相册内存缓存
let albums_cache = Arc::new(tokio::sync::RwLock::new(AlbumsCache::new()));
-
+
// 初始加载相册数据到缓存
let initial_cache = Arc::clone(&albums_cache);
let initial_db = Arc::clone(&db);
@@ -76,10 +76,10 @@ async fn main() -> std::io::Result<()> {
let refresh_db = Arc::clone(&db);
tokio::spawn(async move {
let mut interval = interval(Duration::from_secs(30 * 60)); // 30分钟
-
+
loop {
interval.tick().await;
-
+
match refresh_db.get_all_albums().await {
Ok(albums) => {
let mut cache = refresh_cache.write().await;
@@ -131,10 +131,7 @@ async fn main() -> std::io::Result<()> {
.route("/api/album/{id}", web::get().to(routes::get_album_detail))
.route("/api/albums", web::post().to(routes::create_album))
.route("/api/thumbnail/{id}", web::get().to(routes::get_thumbnail))
- .route(
- "/api/original/{id}",
- web::get().to(routes::get_original_image),
- )
+ .route("/api/original/{id}", web::get().to(routes::get_original_image))
// 收藏功能API路由
.route("/api/favorites", web::get().to(routes::get_favorites))
.route("/api/favorites/status/{id}", web::get().to(routes::check_favorite_status))
diff --git a/src/models.rs b/src/models.rs
index 7518fbd..757696a 100644
--- a/src/models.rs
+++ b/src/models.rs
@@ -12,7 +12,7 @@ pub struct Album {
pub updated_at: String,
}
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Image {
pub id: Uuid,
pub album_id: Uuid,
@@ -147,11 +147,16 @@ impl AlbumsCache {
}
pub fn search(&self, query: &str) -> Vec {
- let query_lower = query.to_lowercase();
+ let query_lower = query.to_lowercase().trim().to_string();
+
+ if query_lower.is_empty() {
+ return Vec::new();
+ }
+
let mut results = Vec::new();
let mut seen_ids = std::collections::HashSet::new();
- // 搜索完整匹配
+ // 1. 搜索完整匹配(最高优先级)
if let Some(matches) = self.search_index.get(&query_lower) {
for album in matches {
if seen_ids.insert(album.id) {
@@ -160,10 +165,45 @@ impl AlbumsCache {
}
}
- // 搜索单词匹配
+ // 2. 搜索单词匹配
for word in query_lower.split_whitespace() {
- if let Some(matches) = self.search_index.get(word) {
- for album in matches {
+ if word.len() >= 2 { // 只搜索长度>=2的单词
+ if let Some(matches) = self.search_index.get(word) {
+ for album in matches {
+ if seen_ids.insert(album.id) {
+ results.push(album.clone());
+ }
+ }
+ }
+ }
+ }
+
+ // 3. 模糊搜索:在相册标题和描述中搜索包含查询词的结果
+ if results.is_empty() {
+ for album in &self.albums {
+ let title_lower = album.title.to_lowercase();
+ let description_lower = album.description.as_ref()
+ .map(|d| d.to_lowercase())
+ .unwrap_or_default();
+
+ // 检查标题或描述是否包含搜索词
+ if title_lower.contains(&query_lower) || description_lower.contains(&query_lower) {
+ if seen_ids.insert(album.id) {
+ results.push(album.clone());
+ }
+ }
+ }
+ }
+
+ // 4. 如果还没有结果,尝试部分匹配(更宽松的搜索)
+ if results.is_empty() && query_lower.len() >= 2 {
+ for album in &self.albums {
+ let title_lower = album.title.to_lowercase();
+
+ // 检查标题是否包含搜索词的任何部分
+ if query_lower.split_whitespace().any(|word|
+ word.len() >= 2 && title_lower.contains(word)
+ ) {
if seen_ids.insert(album.id) {
results.push(album.clone());
}
diff --git a/src/routes.rs b/src/routes.rs
index 92f8f76..28270a8 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -26,6 +26,60 @@ pub async fn get_favorites(db: web::Data>) -> impl Responder {
}
}
+/// 检查缩略图质量并在需要时升级为高质量版本
+async fn check_and_upgrade_thumbnail_quality(
+ config: &crate::config::StorageConfig,
+ image: &Image,
+ original_path: &std::path::Path,
+) -> Result<(), Box> {
+ let thumbnail_path = config.thumbnails_dir.join(image.id.to_string());
+
+ // 检查原始图片是否存在
+ if !original_path.exists() {
+ return Ok(()); // 原始图片不存在,无法升级
+ }
+
+ // 读取缩略图文件信息
+ let thumbnail_metadata = match std::fs::metadata(&thumbnail_path) {
+ Ok(meta) => meta,
+ Err(_) => return Ok(()), // 无法读取缩略图元数据
+ };
+
+ // 检查缩略图文件大小(简单质量检测)
+ // 如果缩略图小于20KB,可能质量较低,需要升级
+ if thumbnail_metadata.len() < 20 * 1024 {
+ // 读取原始图片数据
+ let original_data = match std::fs::read(original_path) {
+ Ok(data) => data,
+ Err(e) => {
+ eprintln!("读取原始图片失败,无法升级缩略图: {}", e);
+ return Ok(());
+ }
+ };
+
+ // 生成高质量缩略图
+ let image_processor = crate::image_processor::ImageProcessor::new(config.clone());
+ match image_processor
+ .generate_thumbnail_from_memory(&original_data, &image.id.to_string())
+ .await
+ {
+ Ok(high_quality_data) => {
+ // 保存高质量缩略图
+ if let Err(e) = std::fs::write(&thumbnail_path, &high_quality_data) {
+ eprintln!("保存高质量缩略图失败: {}", e);
+ } else {
+ println!("✅ 缩略图已升级为高质量版本 (ID: {})", image.id);
+ }
+ }
+ Err(e) => {
+ eprintln!("生成高质量缩略图失败: {}", e);
+ }
+ }
+ }
+
+ Ok(())
+}
+
// 检查图片是否被收藏
pub async fn check_favorite_status(
db: web::Data>,
@@ -179,35 +233,28 @@ pub async fn get_original_image(
}
}
-pub async fn get_albums(
- albums_cache: web::Data>>,
- query: web::Query>,
-) -> impl Responder {
- let page = query.get("page").and_then(|s| s.parse::().ok()).unwrap_or(1);
- let limit = query.get("limit").and_then(|s| s.parse::().ok()).unwrap_or(50);
- let offset = (page - 1) * limit;
-
+pub async fn get_albums(albums_cache: web::Data>>) -> impl Responder {
// 从内存缓存获取数据
let cache = albums_cache.read().await;
let total_albums = cache.albums.len();
-
+
// 应用分页
- let start = offset.min(total_albums);
- let end = (offset + limit).min(total_albums);
+ let start = 0;
+ let end = total_albums;
let paginated_albums = if start < end {
cache.albums[start..end].to_vec()
} else {
Vec::new()
};
-
+
drop(cache); // 释放读锁
-
+
HttpResponse::Ok().json(paginated_albums)
}
pub async fn search_albums(
- albums_cache: web::Data>>,
- query: web::Query>,
+ albums_cache: web::Data>>,
+ query: web::Query>,
) -> impl Responder {
let q = query.get("q").map(|s| s.as_str()).unwrap_or("");
let page = query
@@ -262,22 +309,22 @@ pub async fn get_random_album(
) -> impl Responder {
// 从内存缓存获取数据
let cache = albums_cache.read().await;
-
+
if cache.albums.is_empty() {
drop(cache);
return HttpResponse::NotFound().json(serde_json::json!({
"error": "没有可用的相册"
}));
}
-
+
// 使用随机数生成器选择一个随机相册
use rand::Rng;
let mut rng = rand::thread_rng();
let random_index = rng.gen_range(0..cache.albums.len());
let random_album = cache.albums[random_index].clone();
-
+
drop(cache);
-
+
HttpResponse::Ok().json(random_album)
}
@@ -350,10 +397,25 @@ pub async fn get_thumbnail(
let original_path = image_processor.config.images_dir.join(&image.filename);
if thumbnail_path.exists() {
- // 返回缩略图
+ // 返回现有缩略图,并异步检查是否需要替换为高质量版本
match fs::read(&thumbnail_path) {
Ok(data) => {
let mime_type = image_processor.detect_mime_type("jpg"); // 缩略图总是JPEG格式
+
+ // 异步检查缩略图质量并可能替换为高质量版本
+ let config_clone = image_processor.config.clone();
+ let image_clone = image.clone();
+ let original_path_clone = original_path.clone();
+ tokio::spawn(async move {
+ if let Err(e) = check_and_upgrade_thumbnail_quality(
+ &config_clone,
+ &image_clone,
+ &original_path_clone
+ ).await {
+ eprintln!("缩略图质量检查失败: {}", e);
+ }
+ });
+
HttpResponse::Ok()
.content_type(mime_type)
.insert_header(("Cache-Control", "public, max-age=31536000")) // 1年缓存
@@ -405,7 +467,7 @@ pub async fn get_thumbnail(
// 返回快速生成的缩略图数据
HttpResponse::Ok()
.content_type("image/jpeg")
- .insert_header(("Cache-Control", "public, max-age=31536000")) // 1年缓存
+ .insert_header(("Cache-Control", "public, max-age=30")) // 0.5分钟缓存
.insert_header(("ETag", format!("\"{}\"", image_id)))
.body(thumbnail_data)
}