You've already forked DataMate
feat(data-annotation): 扩展文件预览功能支持多媒体格式
- 添加对图片、视频、音频文件的预览支持 - 新增文件类型检测逻辑,支持多种媒体格式扩展名 - 实现不同文件类型的预览界面适配 - 更新预览弹窗以支持文本、图像、音视频内容展示 - 调整预览窗口尺寸以优化不同媒体类型显示效果 - 完善预览组件卸载时的状态清理机制
This commit is contained in:
@@ -48,6 +48,8 @@ export default function CreateAnnotationTask({
|
|||||||
const [fileContent, setFileContent] = useState("");
|
const [fileContent, setFileContent] = useState("");
|
||||||
const [fileContentLoading, setFileContentLoading] = useState(false);
|
const [fileContentLoading, setFileContentLoading] = useState(false);
|
||||||
const [previewFileName, setPreviewFileName] = useState("");
|
const [previewFileName, setPreviewFileName] = useState("");
|
||||||
|
const [previewFileType, setPreviewFileType] = useState<"text" | "image" | "video" | "audio">("text");
|
||||||
|
const [previewMediaUrl, setPreviewMediaUrl] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open) return;
|
if (!open) return;
|
||||||
@@ -121,30 +123,57 @@ export default function CreateAnnotationTask({
|
|||||||
|
|
||||||
// 预览文件内容
|
// 预览文件内容
|
||||||
const handlePreviewFileContent = async (file: any) => {
|
const handlePreviewFileContent = async (file: any) => {
|
||||||
// 支持预览的文本文件类型
|
|
||||||
const textExtensions = ['.json', '.jsonl', '.txt', '.csv', '.tsv', '.xml', '.md', '.yaml', '.yml'];
|
|
||||||
const fileName = file.fileName?.toLowerCase() || '';
|
const fileName = file.fileName?.toLowerCase() || '';
|
||||||
const isTextFile = textExtensions.some(ext => fileName.endsWith(ext));
|
|
||||||
|
|
||||||
if (!isTextFile) {
|
// 文件类型扩展名映射
|
||||||
message.warning("仅支持预览文本类文件(JSON、JSONL、TXT、CSV 等)");
|
const textExtensions = ['.json', '.jsonl', '.txt', '.csv', '.tsv', '.xml', '.md', '.yaml', '.yml'];
|
||||||
|
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'];
|
||||||
|
const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi'];
|
||||||
|
const audioExtensions = ['.mp3', '.wav', '.ogg', '.aac', '.flac', '.m4a'];
|
||||||
|
|
||||||
|
const isTextFile = textExtensions.some(ext => fileName.endsWith(ext));
|
||||||
|
const isImageFile = imageExtensions.some(ext => fileName.endsWith(ext));
|
||||||
|
const isVideoFile = videoExtensions.some(ext => fileName.endsWith(ext));
|
||||||
|
const isAudioFile = audioExtensions.some(ext => fileName.endsWith(ext));
|
||||||
|
|
||||||
|
if (!isTextFile && !isImageFile && !isVideoFile && !isAudioFile) {
|
||||||
|
message.warning("不支持预览该文件类型");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileContentLoading(true);
|
setFileContentLoading(true);
|
||||||
setPreviewFileName(file.fileName);
|
setPreviewFileName(file.fileName);
|
||||||
|
|
||||||
|
const fileUrl = `/api/data-management/datasets/${selectedDatasetId}/files/${file.id}/download`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/data-management/datasets/${selectedDatasetId}/files/${file.id}/download`);
|
if (isTextFile) {
|
||||||
if (!response.ok) {
|
// 文本文件:获取内容
|
||||||
throw new Error('下载失败');
|
const response = await fetch(fileUrl);
|
||||||
}
|
if (!response.ok) {
|
||||||
const text = await response.text();
|
throw new Error('下载失败');
|
||||||
// 限制预览内容长度,避免大文件导致页面卡顿
|
}
|
||||||
const maxLength = 50000;
|
const text = await response.text();
|
||||||
if (text.length > maxLength) {
|
// 限制预览内容长度
|
||||||
setFileContent(text.substring(0, maxLength) + '\n\n... (内容过长,仅显示前 50000 字符)');
|
const maxLength = 50000;
|
||||||
} else {
|
if (text.length > maxLength) {
|
||||||
setFileContent(text);
|
setFileContent(text.substring(0, maxLength) + '\n\n... (内容过长,仅显示前 50000 字符)');
|
||||||
|
} else {
|
||||||
|
setFileContent(text);
|
||||||
|
}
|
||||||
|
setPreviewFileType("text");
|
||||||
|
} else if (isImageFile) {
|
||||||
|
// 图片文件:直接使用 URL
|
||||||
|
setPreviewMediaUrl(fileUrl);
|
||||||
|
setPreviewFileType("image");
|
||||||
|
} else if (isVideoFile) {
|
||||||
|
// 视频文件:使用 URL
|
||||||
|
setPreviewMediaUrl(fileUrl);
|
||||||
|
setPreviewFileType("video");
|
||||||
|
} else if (isAudioFile) {
|
||||||
|
// 音频文件:使用 URL
|
||||||
|
setPreviewMediaUrl(fileUrl);
|
||||||
|
setPreviewFileType("audio");
|
||||||
}
|
}
|
||||||
setFileContentVisible(true);
|
setFileContentVisible(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -574,7 +603,7 @@ export default function CreateAnnotationTask({
|
|||||||
</Button>
|
</Button>
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<div className="mb-2 text-xs text-gray-500">点击文件名可预览文本类文件内容</div>
|
<div className="mb-2 text-xs text-gray-500">点击文件名可预览文件内容(支持文本、图片、音频、视频)</div>
|
||||||
<Table
|
<Table
|
||||||
dataSource={datasetPreviewData}
|
dataSource={datasetPreviewData}
|
||||||
columns={[
|
columns={[
|
||||||
@@ -618,29 +647,66 @@ export default function CreateAnnotationTask({
|
|||||||
{/* 文件内容预览弹窗 */}
|
{/* 文件内容预览弹窗 */}
|
||||||
<Modal
|
<Modal
|
||||||
open={fileContentVisible}
|
open={fileContentVisible}
|
||||||
onCancel={() => setFileContentVisible(false)}
|
onCancel={() => {
|
||||||
|
setFileContentVisible(false);
|
||||||
|
setPreviewMediaUrl("");
|
||||||
|
setFileContent("");
|
||||||
|
}}
|
||||||
title={`文件预览:${previewFileName}`}
|
title={`文件预览:${previewFileName}`}
|
||||||
width={800}
|
width={previewFileType === "text" ? 800 : 700}
|
||||||
footer={[
|
footer={[
|
||||||
<Button key="close" onClick={() => setFileContentVisible(false)}>
|
<Button key="close" onClick={() => {
|
||||||
|
setFileContentVisible(false);
|
||||||
|
setPreviewMediaUrl("");
|
||||||
|
setFileContent("");
|
||||||
|
}}>
|
||||||
关闭
|
关闭
|
||||||
</Button>
|
</Button>
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<pre
|
{previewFileType === "text" && (
|
||||||
style={{
|
<pre
|
||||||
maxHeight: '500px',
|
style={{
|
||||||
overflow: 'auto',
|
maxHeight: '500px',
|
||||||
backgroundColor: '#f5f5f5',
|
overflow: 'auto',
|
||||||
padding: '12px',
|
backgroundColor: '#f5f5f5',
|
||||||
borderRadius: '4px',
|
padding: '12px',
|
||||||
fontSize: '12px',
|
borderRadius: '4px',
|
||||||
whiteSpace: 'pre-wrap',
|
fontSize: '12px',
|
||||||
wordBreak: 'break-all',
|
whiteSpace: 'pre-wrap',
|
||||||
}}
|
wordBreak: 'break-all',
|
||||||
>
|
}}
|
||||||
{fileContent}
|
>
|
||||||
</pre>
|
{fileContent}
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
{previewFileType === "image" && (
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<img
|
||||||
|
src={previewMediaUrl}
|
||||||
|
alt={previewFileName}
|
||||||
|
style={{ maxWidth: '100%', maxHeight: '500px', objectFit: 'contain' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{previewFileType === "video" && (
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<video
|
||||||
|
src={previewMediaUrl}
|
||||||
|
controls
|
||||||
|
style={{ maxWidth: '100%', maxHeight: '500px' }}
|
||||||
|
>
|
||||||
|
您的浏览器不支持视频播放
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{previewFileType === "audio" && (
|
||||||
|
<div style={{ textAlign: 'center', padding: '40px 0' }}>
|
||||||
|
<audio src={previewMediaUrl} controls style={{ width: '100%' }}>
|
||||||
|
您的浏览器不支持音频播放
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user