检查文件是否存在

This commit is contained in:
root 2025-07-11 11:39:11 +08:00
parent 47b156c68a
commit 44a669b6fc
4 changed files with 111 additions and 18 deletions

View File

@ -81,6 +81,39 @@ pub async fn read_video_as_data_url(file_path: String) -> Result<String, String>
} }
} }
/// 检查文件是否存在
#[command]
pub async fn check_file_exists(file_path: String) -> Result<bool, String> {
let path = Path::new(&file_path);
Ok(path.exists())
}
/// 获取文件信息
#[command]
pub async fn get_file_info(file_path: String) -> Result<serde_json::Value, String> {
let path = Path::new(&file_path);
if !path.exists() {
return Err("File does not exist".to_string());
}
match fs::metadata(&file_path) {
Ok(metadata) => {
let info = serde_json::json!({
"exists": true,
"size": metadata.len(),
"is_file": metadata.is_file(),
"is_dir": metadata.is_dir(),
"modified": metadata.modified()
.map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs())
.unwrap_or(0)
});
Ok(info)
}
Err(e) => Err(format!("Failed to get file metadata: {}", e))
}
}
/// 获取视频文件的blob URL用于大文件 /// 获取视频文件的blob URL用于大文件
#[command] #[command]
pub async fn get_video_blob_url(file_path: String) -> Result<String, String> { pub async fn get_video_blob_url(file_path: String) -> Result<String, String> {

View File

@ -84,7 +84,9 @@ pub fn run() {
commands::media::delete_original_video, commands::media::delete_original_video,
commands::file_utils::read_image_as_data_url, commands::file_utils::read_image_as_data_url,
commands::file_utils::read_video_as_data_url, commands::file_utils::read_video_as_data_url,
commands::file_utils::get_video_blob_url commands::file_utils::get_video_blob_url,
commands::file_utils::check_file_exists,
commands::file_utils::get_file_info
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

View File

@ -25,7 +25,7 @@
} }
], ],
"security": { "security": {
"csp": "default-src 'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost data:; media-src 'self' asset: http://asset.localhost; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'", "csp": "default-src 'self' ipc: http://ipc.localhost; img-src 'self' asset: http://asset.localhost data:; media-src 'self' asset: http://asset.localhost; video-src 'self' asset: http://asset.localhost; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
"assetProtocol": { "assetProtocol": {
"enable": true, "enable": true,
"scope": ["**"] "scope": ["**"]

View File

@ -1,6 +1,6 @@
import React, { useState, useRef, useEffect } from 'react' import React, { useState, useRef, useEffect } from 'react'
import { Play, Pause, Volume2, VolumeX, Maximize2, X } from 'lucide-react' import { Play, Pause, Volume2, VolumeX, Maximize2, X, AlertCircle } from 'lucide-react'
import { convertFileSrc } from '@tauri-apps/api/core' import { convertFileSrc, invoke } from '@tauri-apps/api/core'
interface VideoPlayerProps { interface VideoPlayerProps {
videoPath: string videoPath: string
@ -23,14 +23,43 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
const [volume, setVolume] = useState(1) const [volume, setVolume] = useState(1)
const [isFullscreen, setIsFullscreen] = useState(false) const [isFullscreen, setIsFullscreen] = useState(false)
const [videoSrc, setVideoSrc] = useState<string>('') const [videoSrc, setVideoSrc] = useState<string>('')
const [fileExists, setFileExists] = useState<boolean>(true)
const [errorMessage, setErrorMessage] = useState<string>('')
useEffect(() => { useEffect(() => {
const checkFileAndSetSrc = async () => {
if (isOpen && videoPath) { if (isOpen && videoPath) {
// 使用Tauri的convertFileSrc来获取安全的文件URL try {
const src = convertFileSrc(videoPath) // 首先检查文件是否存在
console.log({ src }) const exists = await invoke<boolean>('check_file_exists', { filePath: videoPath })
setVideoSrc(src) console.log('File exists check:', { videoPath, exists })
if (!exists) {
setFileExists(false)
setErrorMessage(`文件不存在: ${videoPath}`)
return
} }
// 文件存在转换为安全URL
const src = convertFileSrc(videoPath)
console.log('Video path conversion:', {
originalPath: videoPath,
convertedSrc: src,
fileExists: exists
})
setFileExists(true)
setErrorMessage('')
setVideoSrc(src)
} catch (error) {
console.error('Error checking file:', error)
setFileExists(false)
setErrorMessage(`文件检查失败: ${error}`)
}
}
}
checkFileAndSetSrc()
}, [isOpen, videoPath]) }, [isOpen, videoPath])
useEffect(() => { useEffect(() => {
@ -146,15 +175,44 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
{/* 视频容器 */} {/* 视频容器 */}
<div className="relative bg-black rounded-lg overflow-hidden"> <div className="relative bg-black rounded-lg overflow-hidden">
{!fileExists || errorMessage ? (
/* 错误显示 */
<div className="flex items-center justify-center h-96 text-white">
<div className="text-center">
<AlertCircle size={64} className="mx-auto mb-4 text-red-400" />
<h3 className="text-xl font-medium mb-2"></h3>
<p className="text-gray-300 mb-4">{errorMessage}</p>
<p className="text-sm text-gray-400">: {videoPath}</p>
</div>
</div>
) : (
<video <video
ref={videoRef} ref={videoRef}
src={videoSrc} src={videoSrc}
className="w-full h-auto max-h-[70vh]" className="w-full h-auto max-h-[70vh]"
onClick={handlePlayPause} onClick={handlePlayPause}
onError={(e) => { onError={(e) => {
console.error('Video error:', e) console.error('Video loading error:', {
error: e,
videoSrc,
originalPath: videoPath,
currentTarget: e.currentTarget,
networkState: e.currentTarget.networkState,
readyState: e.currentTarget.readyState
})
setErrorMessage('视频加载失败,请检查文件格式和路径')
}}
onLoadStart={() => {
console.log('Video load started:', videoSrc)
}}
onCanPlay={() => {
console.log('Video can play:', videoSrc)
}}
onLoadedData={() => {
console.log('Video data loaded:', videoSrc)
}} }}
/> />
)}
{/* 控制栏 */} {/* 控制栏 */}
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4"> <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-4">