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

View File

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

View File

@ -47,11 +47,35 @@ export class UploadController {
} }
@Get('share/:code') @Get('share/:code')
async validateShareCode(@Param('code') code: string) { async validateShareCode(@Param('code') code: string) {
const fileId = await this.shareCodeService.validateAndUseCode(code); console.log('收到验证分享码请求code:', code);
if (!fileId) {
const shareCode = await this.shareCodeService.validateAndUseCode(code);
console.log('验证分享码结果:', shareCode);
if (!shareCode) {
throw new NotFoundException('分享码无效或已过期'); 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') @Get('share/info/:code')

View File

@ -9,12 +9,13 @@ const { TabPane } = Tabs;
export default function DeptSettingPage() { export default function DeptSettingPage() {
const [uploadedFileId, setUploadedFileId] = useState<string>(''); const [uploadedFileId, setUploadedFileId] = useState<string>('');
const [fileNameMap, setFileNameMap] = useState<Record<string, string>>({}); const [uploadedFileName, setUploadedFileName] = useState<string>('');
// 使用您的 useTusUpload hook // 使用您的 useTusUpload hook
const { uploadProgress, isUploading, uploadError, handleFileUpload } = useTusUpload({ const { uploadProgress, isUploading, uploadError, handleFileUpload } = useTusUpload({
onSuccess: (fileId: string) => { onSuccess: (result) => {
setUploadedFileId(fileId); setUploadedFileId(result.fileId);
setUploadedFileName(result.fileName);
message.success('文件上传成功'); message.success('文件上传成功');
}, },
onError: (error: Error) => { onError: (error: Error) => {
@ -30,18 +31,12 @@ export default function DeptSettingPage() {
file, file,
async (result) => { async (result) => {
setUploadedFileId(result.fileId); setUploadedFileId(result.fileId);
setUploadedFileName(result.fileName);
// 在前端保存文件名映射(用于当前会话)
setFileNameMap(prev => ({
...prev,
[result.fileId]: file.name
}));
// 上传成功后保存原始文件名到数据库
try { 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', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -52,6 +47,7 @@ export default function DeptSettingPage() {
}), }),
}); });
const responseText = await response.text(); const responseText = await response.text();
console.log('保存文件名响应:', response.status, responseText); 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 { try {
// 构建下载URL // 构建下载URL包含文件名参数
const response = await fetch(`/api/upload/download/${fileId}`); const downloadUrl = `/upload/download/${fileId}?fileName=${encodeURIComponent(fileName)}`;
const response = await fetch(downloadUrl);
if (!response.ok) { if (!response.ok) {
throw new Error('文件下载失败'); 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 blob = await response.blob();
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
const link = document.createElement('a'); const link = document.createElement('a');
link.href = url; link.href = url;
// 使用获取到的文件名或本地缓存的文件名 // 直接使用传入的 fileName
link.download = filename; link.download = fileName;
// 触发下载 // 触发下载
document.body.appendChild(link); document.body.appendChild(link);

View File

@ -6,11 +6,13 @@ import { CopyOutlined } from '@ant-design/icons';
interface ShareCodeGeneratorProps { interface ShareCodeGeneratorProps {
fileId: string; fileId: string;
onSuccess?: (code: string) => void; onSuccess?: (code: string) => void;
fileName?: string;
} }
export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
fileId, fileId,
onSuccess, onSuccess,
fileName,
}) => { }) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [shareCode, setShareCode] = useState<string>(''); const [shareCode, setShareCode] = useState<string>('');
@ -18,16 +20,17 @@ export const ShareCodeGenerator: React.FC<ShareCodeGeneratorProps> = ({
const generateCode = async () => { const generateCode = async () => {
setLoading(true); setLoading(true);
console.log('开始生成分享码fileId:', fileId); console.log('开始生成分享码fileId:', fileId, 'fileName:', fileName);
try { try {
const response = await fetch(`http://localhost:3000/upload/share/${fileId}`, { const response = await fetch(`http://localhost:3000/upload/share/${fileId}`, {
method: 'POST', method: 'POST',
// headers: { headers: {
// 'Content-Type': 'application/json', 'Content-Type': 'application/json',
// }, },
// body: JSON.stringify({ body: JSON.stringify({
// }) fileId
})
}); });
console.log('Current fileId:', fileId); // 确保 fileId 有效 console.log('Current fileId:', fileId); // 确保 fileId 有效
console.log('请求URL:', `/upload/share/${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'; import styles from './ShareCodeValidator.module.css';
interface ShareCodeValidatorProps { interface ShareCodeValidatorProps {
onValidSuccess: (fileId: string) => void; onValidSuccess: (fileId: string, fileName: string) => void;
} }
export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({ export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({
@ -26,10 +26,20 @@ export const ShareCodeValidator: React.FC<ShareCodeValidatorProps> = ({
const errorData = await response.json().catch(() => ({})); const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || '分享码无效或已过期'); throw new Error(errorData.message || '分享码无效或已过期');
} }
const data = await response.json(); const data = await response.json();
onValidSuccess(data.fileId); console.log('验证分享码返回数据:', data);
message.success('验证成功');
if (!data.fileId) {
throw new Error('未找到文件ID');
}
const fileName = data.fileName || 'downloaded_file';
onValidSuccess(data.fileId, fileName);
message.success(`验证成功,文件名:${fileName}`);
} catch (error) { } catch (error) {
console.error('验证分享码失败:', error);
message.error('分享码无效或已过期'); message.error('分享码无效或已过期');
} finally { } finally {
setLoading(false); setLoading(false);

View File

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