lin
This commit is contained in:
parent
bed7a4e690
commit
e746d84fa9
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
|
||||||
throw new NotFoundException('分享码无效或已过期');
|
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')
|
@Get('share/info/:code')
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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}`);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue