update code level
This commit is contained in:
61
Cargo.lock
generated
61
Cargo.lock
generated
@@ -1237,6 +1237,8 @@ dependencies = [
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -1483,6 +1485,15 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.6"
|
||||
@@ -2132,6 +2143,15 @@ dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
@@ -2534,6 +2554,15 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.45"
|
||||
@@ -2697,6 +2726,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2816,6 +2871,12 @@ version = "0.15.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
||||
@@ -15,4 +15,6 @@ uuid = { version = "1.0", features = ["v4", "serde"] }
|
||||
image = { version = "0.24", default-features = false, features = ["jpeg", "png", "gif", "webp"] }
|
||||
reqwest = { version = "0.11", default-features = false, features = ["blocking","rustls-tls"] }
|
||||
config = "0.13"
|
||||
rand = "0.8"
|
||||
rand = "0.8"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = "0.3"
|
||||
@@ -1247,9 +1247,43 @@ body {
|
||||
.search-hint h3 {
|
||||
color: var(--text-dark);
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
/* 加载状态样式 */
|
||||
.loading-state {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: var(--text-light);
|
||||
font-size: 1.2rem;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.loading-state .fa-spinner {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 20px;
|
||||
color: var(--secondary-blue);
|
||||
}
|
||||
|
||||
.loading-state p {
|
||||
margin-bottom: 20px;
|
||||
color: var(--text-dark);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background-color: var(--light-gray);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress {
|
||||
height: 100%;
|
||||
background: linear-gradient(135deg, var(--secondary-blue) 0%, var(--primary-blue) 100%);
|
||||
border-radius: 3px;
|
||||
transition: width 0.3s ease;}
|
||||
|
||||
.search-hint p {
|
||||
margin-bottom: 15px;
|
||||
font-weight: 500;
|
||||
|
||||
@@ -37,7 +37,12 @@ function initSearch() {
|
||||
// 防抖搜索
|
||||
searchInput.addEventListener('input', function () {
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(debouncedSearch, 300);
|
||||
|
||||
// 优化:根据输入长度动态调整延迟
|
||||
const inputLength = this.value.length;
|
||||
const debounceDelay = inputLength > 3 ? 200 : 500; // 长文本快速搜索,短文本慢速搜索
|
||||
|
||||
searchTimeout = setTimeout(debouncedSearch, debounceDelay);
|
||||
});
|
||||
|
||||
// 移动端优化:搜索框聚焦时自动滚动到可视区域
|
||||
@@ -128,6 +133,22 @@ function showSkeleton(count = 6) {
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// 显示详细加载状态
|
||||
function showLoadingState(message = '加载中...', progress = 0) {
|
||||
const container = document.getElementById('albums-container');
|
||||
const progressWidth = Math.min(Math.max(progress, 0), 100);
|
||||
|
||||
container.innerHTML = `
|
||||
<div class="loading-state">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
<p>${message}</p>
|
||||
<div class="progress-bar">
|
||||
<div class="progress" style="width: ${progressWidth}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 显示加载更多指示器
|
||||
function showLoadingMore() {
|
||||
const container = document.getElementById('albums-container');
|
||||
@@ -398,8 +419,8 @@ function initLazyLoading() {
|
||||
}
|
||||
});
|
||||
}, {
|
||||
rootMargin: '100px 0px', // 提前100px开始加载
|
||||
threshold: 0.01
|
||||
rootMargin: '50px', // 优化:提前50px开始加载
|
||||
threshold: 0.1 // 优化:降低触发阈值
|
||||
});
|
||||
}
|
||||
|
||||
@@ -456,17 +477,30 @@ async function searchAlbums() {
|
||||
// 性能监控
|
||||
function logPerformance() {
|
||||
if ('performance' in window) {
|
||||
window.addEventListener('load', () => {
|
||||
setTimeout(() => {
|
||||
const navigationTiming = performance.getEntriesByType('navigation')[0];
|
||||
if (navigationTiming && navigationTiming.loadEventEnd && navigationTiming.navigationStart) {
|
||||
const loadTime = navigationTiming.loadEventEnd - navigationTiming.navigationStart;
|
||||
console.log('页面加载时间:', loadTime, 'ms');
|
||||
// 使用更可靠的方式获取页面加载时间
|
||||
const calculateLoadTime = () => {
|
||||
const navigationTiming = performance.getEntriesByType('navigation')[0];
|
||||
if (navigationTiming && navigationTiming.loadEventEnd > 0 && navigationTiming.navigationStart > 0) {
|
||||
const loadTime = navigationTiming.loadEventEnd - navigationTiming.navigationStart;
|
||||
console.log('页面加载时间:', loadTime, 'ms');
|
||||
} else {
|
||||
// 备用方案:使用DOMContentLoaded时间
|
||||
const domContentLoadedTime = performance.timing?.domContentLoadedEventEnd - performance.timing?.navigationStart;
|
||||
if (domContentLoadedTime && domContentLoadedTime > 0) {
|
||||
console.log('DOM加载时间:', domContentLoadedTime, 'ms');
|
||||
} else {
|
||||
console.log('页面加载时间: 无法计算');
|
||||
}
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 如果页面已经加载完成,直接计算
|
||||
if (document.readyState === 'complete') {
|
||||
calculateLoadTime();
|
||||
} else {
|
||||
// 否则等待页面加载完成
|
||||
window.addEventListener('load', calculateLoadTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,18 +520,18 @@ function preventDoubleTapZoom() {
|
||||
|
||||
// 预加载关键资源
|
||||
function preloadCriticalResources() {
|
||||
// 预加载关键CSS和字体
|
||||
const criticalResources = [
|
||||
'/assets/css/font-awesome.css',
|
||||
'/assets/css/style.css',
|
||||
'/assets/webfonts/fa-solid-900.woff2'
|
||||
// 预加载字体文件(CSS文件不应该预加载,因为已经在HTML中引用了)
|
||||
const fontResources = [
|
||||
'/assets/webfonts/fa-solid-900.woff2',
|
||||
'/assets/webfonts/fa-brands-400.woff2',
|
||||
'/assets/webfonts/fa-regular-400.woff2'
|
||||
];
|
||||
|
||||
criticalResources.forEach(resource => {
|
||||
fontResources.forEach(resource => {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'preload';
|
||||
link.href = resource;
|
||||
link.as = resource.endsWith('.css') ? 'style' : 'font';
|
||||
link.as = 'font';
|
||||
link.crossOrigin = 'anonymous';
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
|
||||
@@ -33,11 +33,11 @@ impl Database {
|
||||
}
|
||||
|
||||
let pool = SqlitePoolOptions::new()
|
||||
.max_connections(20) // 增加连接数以支持并发请求
|
||||
.min_connections(5) // 保持更多连接以减少连接建立时间
|
||||
.acquire_timeout(std::time::Duration::from_secs(10)) // 减少获取连接超时时间
|
||||
.idle_timeout(std::time::Duration::from_secs(600)) // 增加空闲超时时间
|
||||
.max_lifetime(std::time::Duration::from_secs(3600)) // 增加最大生命周期
|
||||
.max_connections(10) // 优化:减少最大连接数,避免资源浪费
|
||||
.min_connections(2) // 优化:减少最小连接数
|
||||
.acquire_timeout(std::time::Duration::from_secs(5)) // 优化:减少获取连接超时时间
|
||||
.idle_timeout(std::time::Duration::from_secs(300)) // 优化:减少空闲超时时间
|
||||
.max_lifetime(std::time::Duration::from_secs(1800)) // 优化:减少最大生命周期
|
||||
.connect(url)
|
||||
.await?;
|
||||
|
||||
|
||||
20
src/main.rs
20
src/main.rs
@@ -10,30 +10,34 @@ use actix_web::{App, HttpResponse, HttpServer, web, middleware};
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
use tokio::time::{interval, Duration};
|
||||
use tracing::{info, error, warn, debug};
|
||||
use crate::models::AlbumsCache;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
// 初始化结构化日志
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
// 加载配置
|
||||
let config = match config::Config::load() {
|
||||
Ok(cfg) => cfg,
|
||||
Err(e) => {
|
||||
eprintln!("加载配置失败: {}", e);
|
||||
error!("加载配置失败: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
println!("启动图片展示服务...");
|
||||
println!("服务地址: http://{}", config.server_address());
|
||||
info!("启动图片展示服务...");
|
||||
info!("服务地址: http://{}", config.server_address());
|
||||
|
||||
// 初始化数据库
|
||||
let db = match database::Database::new(&config).await {
|
||||
Ok(db) => {
|
||||
println!("数据库连接成功");
|
||||
info!("数据库连接成功");
|
||||
Arc::new(db)
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("数据库连接失败: {}", e);
|
||||
error!("数据库连接失败: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
@@ -42,10 +46,10 @@ async fn main() -> std::io::Result<()> {
|
||||
let config_clone = config.clone();
|
||||
let db_clone = Arc::clone(&db);
|
||||
tokio::spawn(async move {
|
||||
println!("[后台任务] 开始扫描图片目录并导入新相册...");
|
||||
info!("[后台任务] 开始扫描图片目录并导入新相册...");
|
||||
match import::import_existing_data(&config_clone, db_clone).await {
|
||||
Ok(_) => println!("[后台任务] 相册导入完成"),
|
||||
Err(e) => eprintln!("[后台任务] 相册导入失败: {}", e),
|
||||
Ok(_) => info!("[后台任务] 相册导入完成"),
|
||||
Err(e) => error!("[后台任务] 相册导入失败: {}", e),
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
use tracing::{info, error, warn, debug};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Album {
|
||||
@@ -142,8 +144,8 @@ impl AlbumsCache {
|
||||
.push(album.clone());
|
||||
}
|
||||
|
||||
println!("✅ 相册缓存已更新,共 {} 个相册,{} 个搜索索引项",
|
||||
self.albums.len(), self.search_index.len());
|
||||
info!("相册缓存已更新,共 {} 个相册,{} 个搜索索引项",
|
||||
self.albums.len(), self.search_index.len());
|
||||
}
|
||||
|
||||
pub fn search(&self, query: &str) -> Vec<AlbumInfo> {
|
||||
|
||||
118
src/routes.rs
118
src/routes.rs
@@ -2,11 +2,47 @@ use crate::database::Database;
|
||||
use crate::image_processor::ImageProcessor;
|
||||
use crate::models::*;
|
||||
use actix_web::{HttpResponse, Responder, web};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::error;
|
||||
use uuid::Uuid;
|
||||
// 统一错误处理宏
|
||||
macro_rules! handle_db_error {
|
||||
($result:expr, $error_msg:expr) => {
|
||||
match $result {
|
||||
Ok(value) => value,
|
||||
Err(e) => {
|
||||
error!("{}: {}", $error_msg, e);
|
||||
return HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": $error_msg
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 输入验证错误类型
|
||||
#[derive(Debug)]
|
||||
enum ValidationError {
|
||||
QueryTooLong,
|
||||
InvalidCharacters,
|
||||
}
|
||||
|
||||
// 搜索查询验证函数
|
||||
fn validate_search_query(query: &str) -> Result<(), ValidationError> {
|
||||
if query.len() > 100 {
|
||||
return Err(ValidationError::QueryTooLong);
|
||||
}
|
||||
|
||||
// 允许Unicode字符(包括中文)、ASCII图形字符和空白字符
|
||||
// 只阻止控制字符和其他潜在危险字符
|
||||
if query.chars().any(|c| c.is_control() || c == '\0') {
|
||||
return Err(ValidationError::InvalidCharacters);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 收藏功能API路由
|
||||
|
||||
@@ -15,15 +51,9 @@ pub async fn get_favorites(db: web::Data<Arc<Database>>) -> impl Responder {
|
||||
// 这里使用固定用户ID,实际项目中应该从认证信息中获取
|
||||
let user_id = "default_user".to_string();
|
||||
|
||||
match db.get_user_favorites(&user_id).await {
|
||||
Ok(favorites) => HttpResponse::Ok().json(favorites),
|
||||
Err(e) => {
|
||||
eprintln!("获取收藏列表失败: {}", e);
|
||||
HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": "获取收藏列表失败"
|
||||
}))
|
||||
}
|
||||
}
|
||||
let favorites = handle_db_error!(db.get_user_favorites(&user_id).await, "获取收藏列表失败");
|
||||
|
||||
HttpResponse::Ok().json(favorites)
|
||||
}
|
||||
|
||||
/// 检查缩略图质量并在需要时升级为高质量版本
|
||||
@@ -33,39 +63,39 @@ async fn check_and_upgrade_thumbnail_quality(
|
||||
original_path: &std::path::Path,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
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) {
|
||||
let thumbnail_metadata = match 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) {
|
||||
let original_data = match fs::read(original_path) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
eprintln!("读取原始图片失败,无法升级缩略图: {}", e);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 生成高质量缩略图
|
||||
let image_processor = crate::image_processor::ImageProcessor::new(config.clone());
|
||||
let 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) {
|
||||
if let Err(e) = fs::write(&thumbnail_path, &high_quality_data) {
|
||||
eprintln!("保存高质量缩略图失败: {}", e);
|
||||
} else {
|
||||
println!("✅ 缩略图已升级为高质量版本 (ID: {})", image.id);
|
||||
@@ -76,7 +106,7 @@ async fn check_and_upgrade_thumbnail_quality(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -97,15 +127,12 @@ pub async fn check_favorite_status(
|
||||
// 这里使用固定用户ID,实际项目中应该从认证信息中获取
|
||||
let user_id = "default_user".to_string();
|
||||
|
||||
match db.get_favorite_status(&user_id, image_id).await {
|
||||
Ok(status) => HttpResponse::Ok().json(status),
|
||||
Err(e) => {
|
||||
eprintln!("检查收藏状态失败: {}", e);
|
||||
HttpResponse::InternalServerError().json(serde_json::json!({
|
||||
"error": "检查收藏状态失败"
|
||||
}))
|
||||
}
|
||||
}
|
||||
let status = handle_db_error!(
|
||||
db.get_favorite_status(&user_id, image_id).await,
|
||||
"检查收藏状态失败"
|
||||
);
|
||||
|
||||
HttpResponse::Ok().json(status)
|
||||
}
|
||||
|
||||
// 添加收藏
|
||||
@@ -254,9 +281,24 @@ pub async fn get_albums(albums_cache: web::Data<Arc<RwLock<AlbumsCache>>>) -> im
|
||||
|
||||
pub async fn search_albums(
|
||||
albums_cache: web::Data<Arc<RwLock<AlbumsCache>>>,
|
||||
query: web::Query<HashMap<String, String>>,
|
||||
query: web::Query<std::collections::HashMap<String, String>>,
|
||||
) -> impl Responder {
|
||||
let q = query.get("q").map(|s| s.as_str()).unwrap_or("");
|
||||
|
||||
// 输入验证
|
||||
if let Err(validation_error) = validate_search_query(q) {
|
||||
return match validation_error {
|
||||
ValidationError::QueryTooLong => HttpResponse::BadRequest().json(serde_json::json!({
|
||||
"error": "搜索查询过长,最多100个字符"
|
||||
})),
|
||||
ValidationError::InvalidCharacters => {
|
||||
HttpResponse::BadRequest().json(serde_json::json!({
|
||||
"error": "搜索查询包含无效字符"
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let page = query
|
||||
.get("page")
|
||||
.and_then(|s| s.parse::<usize>().ok())
|
||||
@@ -305,7 +347,7 @@ pub async fn search_albums(
|
||||
}
|
||||
|
||||
pub async fn get_random_album(
|
||||
albums_cache: web::Data<Arc<RwLock<crate::models::AlbumsCache>>>,
|
||||
albums_cache: web::Data<Arc<RwLock<AlbumsCache>>>,
|
||||
) -> impl Responder {
|
||||
// 从内存缓存获取数据
|
||||
let cache = albums_cache.read().await;
|
||||
@@ -401,21 +443,23 @@ pub async fn get_thumbnail(
|
||||
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 {
|
||||
&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年缓存
|
||||
@@ -505,4 +549,4 @@ pub async fn get_thumbnail(
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user