This commit is contained in:
linfeng 2025-03-21 18:11:36 +08:00
parent bed7a4e690
commit e746d84fa9
7 changed files with 81 additions and 66 deletions

View File

@ -38,16 +38,17 @@ export class ShareCodeService {
});
if (existingShareCode) {
// 更新现有记录
// 更新现有记录,但保留原有文件名
await db.shareCode.update({
where: { fileId },
data: {
fileName: fileName || null,
code,
expiresAt,
isUsed: false,
// 如果提供了文件名且现有记录没有文件名,则更新文件名
...(fileName && !existingShareCode.fileName ? { fileName } : {}),
// 只在没有现有文件名且提供了新文件名时才更新文件名
...(fileName && !existingShareCode.fileName
? { fileName }
: {})
},
});
} else {
@ -75,7 +76,7 @@ export class ShareCodeService {
}
}
async validateAndUseCode(code: string): Promise<string | null> {
async validateAndUseCode(code: string): Promise<ShareCode | null> {
try {
console.log(`尝试验证分享码: ${code}`);
@ -104,7 +105,8 @@ export class ShareCodeService {
// 记录使用日志
this.logger.log(`Share code ${code} used for file ${shareCode.fileId}`);
return shareCode.fileId;
// 返回完整的分享码信息,包括文件名
return shareCode;
} catch (error) {
this.logger.error('Failed to validate share code', error);
return null;

View File

@ -22,12 +22,13 @@ export interface UploadLock {
}
export interface ShareCode {
id: string;
code: string;
fileId: string;
createdAt: Date;
expiresAt: Date;
isUsed: boolean;
fileName?: string;
fileName?: string | null;
}
export interface GenerateShareCodeResponse {

View File

@ -47,11 +47,35 @@ export class UploadController {
}
@Get('share/:code')
async validateShareCode(@Param('code') code: string) {
const fileId = await this.shareCodeService.validateAndUseCode(code);
if (!fileId) {
console.log('收到验证分享码请求code:', code);
const shareCode = await this.shareCodeService.validateAndUseCode(code);
console.log('验证分享码结果:', shareCode);
if (!shareCode) {
throw new NotFoundException('分享码无效或已过期');
}
return { fileId };
// 获取文件信息
const resource = await this.resourceService.findUnique({
where: { fileId: shareCode.fileId },
});
console.log('获取到的资源信息:', resource);
if (!resource) {
throw new NotFoundException('文件不存在');
}
// 直接返回正确的数据结构
const response = {
fileId: shareCode.fileId,
fileName:shareCode.fileName || 'downloaded_file',
code: shareCode.code,
expiresAt: shareCode.expiresAt
};
console.log('返回给前端的数据:', response); // 添加日志
return response;
}
@Get('share/info/:code')

View File

@ -9,12 +9,13 @@ const { TabPane } = Tabs;
export default function DeptSettingPage() {
const [uploadedFileId, setUploadedFileId] = useState<string>('');
const [fileNameMap, setFileNameMap] = useState<Record<string, string>>({});
const [uploadedFileName, setUploadedFileName] = useState<string>('');
// 使用您的 useTusUpload hook
const { uploadProgress, isUploading, uploadError, handleFileUpload } = useTusUpload({
onSuccess: (fileId: string) => {
setUploadedFileId(fileId);
onSuccess: (result) => {
setUploadedFileId(result.fileId);
setUploadedFileName(result.fileName);
message.success('文件上传成功');
},
onError: (error: Error) => {
@ -30,18 +31,12 @@ export default function DeptSettingPage() {
file,
async (result) => {
setUploadedFileId(result.fileId);
setUploadedFileName(result.fileName);
// 在前端保存文件名映射(用于当前会话)
setFileNameMap(prev => ({
...prev,
[result.fileId]: file.name
}));
// 上传成功后保存原始文件名到数据库
try {
console.log('正在保存文件名到数据库:', file.name, '对应文件ID:', result.fileId);
console.log('正在保存文件名到数据库:', result.fileName, '对应文件ID:', result.fileId);
const response = await fetch('/api/upload/filename', {
const response = await fetch('http://localhost:3000/upload/filename', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -52,6 +47,7 @@ export default function DeptSettingPage() {
}),
});
const responseText = await response.text();
console.log('保存文件名响应:', response.status, responseText);
@ -82,46 +78,23 @@ export default function DeptSettingPage() {
};
// 处理分享码验证成功
const handleValidSuccess = async (fileId: string) => {
const handleValidSuccess = async (fileId: string, fileName: string) => {
try {
// 构建下载URL
const response = await fetch(`/api/upload/download/${fileId}`);
// 构建下载URL包含文件名参数
const downloadUrl = `/upload/download/${fileId}?fileName=${encodeURIComponent(fileName)}`;
const response = await fetch(downloadUrl);
if (!response.ok) {
throw new Error('文件下载失败');
}
// 获取文件名
const contentDisposition = response.headers.get('content-disposition');
console.log('Content-Disposition 头:', contentDisposition);
let filename = fileNameMap[fileId] || 'downloaded-file'; // 优先使用本地缓存的文件名
if (contentDisposition) {
// 改进文件名提取逻辑
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(contentDisposition);
if (matches && matches[1]) {
// 移除引号并解码 URL 编码的文件名
filename = matches[1].replace(/['"]/g, '');
try {
// 尝试解码 URL 编码的文件名
filename = decodeURIComponent(filename);
} catch (e) {
console.warn('文件名解码失败,使用原始文件名');
}
}
}
console.log('提取的文件名:', filename);
// 创建下载链接
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
// 使用获取到的文件名或本地缓存的文件名
link.download = filename;
// 直接使用传入的 fileName
link.download = fileName;
// 触发下载
document.body.appendChild(link);

View File

@ -6,11 +6,13 @@ import { CopyOutlined } from '@ant-design/icons';
interface ShareCodeGeneratorProps {
fileId: string;
onSuccess?: (code: string) => void;
fileName?: string;
}
export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
fileId,
onSuccess,
fileName,
}) => {
const [loading, setLoading] = useState(false);
const [shareCode, setShareCode] = useState<string>('');
@ -18,16 +20,17 @@ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
const generateCode = async () => {
setLoading(true);
console.log('开始生成分享码fileId:', fileId);
console.log('开始生成分享码fileId:', fileId, 'fileName:', fileName);
try {
const response = await fetch(`http://localhost:3000/upload/share/${fileId}`, {
method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify({
// })
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
fileId
})
});
console.log('Current fileId:', fileId); // 确保 fileId 有效
console.log('请求URL:', `/upload/share/${fileId}`);

View File

@ -3,7 +3,7 @@ import { Input, Button, message } from 'antd';
import styles from './ShareCodeValidator.module.css';
interface ShareCodeValidatorProps {
onValidSuccess: (fileId: string) => void;
onValidSuccess: (fileId: string, fileName: string) => void;
}
export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({
@ -26,10 +26,20 @@ export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || '分享码无效或已过期');
}
const data = await response.json();
onValidSuccess(data.fileId);
message.success('验证成功');
console.log('验证分享码返回数据:', data);
if (!data.fileId) {
throw new Error('未找到文件ID');
}
const fileName = data.fileName || 'downloaded_file';
onValidSuccess(data.fileId, fileName);
message.success(`验证成功,文件名:${fileName}`);
} catch (error) {
console.error('验证分享码失败:', error);
message.error('分享码无效或已过期');
} finally {
setLoading(false);

View File

@ -7,9 +7,10 @@ interface UploadResult {
compressedUrl: string;
url: string;
fileId: string;
fileName: string;
}
export function useTusUpload(p0: { onSuccess: (fileId: string) => void; onError: (error: Error) => void; }) {
export function useTusUpload(p0: { onSuccess: (result: UploadResult) => void; onError: (error: Error) => void; }) {
const [uploadProgress, setUploadProgress] = useState<
Record<string, number>
>({});
@ -87,6 +88,7 @@ export function useTusUpload(p0: { onSuccess: (fileId: string) => void; onError:
compressedUrl: getCompressedImageUrl(url),
url,
fileId,
fileName: uploadFile.name,
});
}
} catch (error) {