fix: harden uploads downloads and deployment config

This commit is contained in:
2026-03-08 19:40:22 +08:00
parent aa2e6bbe95
commit c23c4ac1e3
7 changed files with 133 additions and 46 deletions

View File

@@ -2,6 +2,24 @@ import axios from 'axios';
import { message } from 'antd';
import { API_CONFIG } from '../utils/config';
const parseFilenameFromDisposition = (contentDisposition) => {
if (!contentDisposition) {
return '';
}
const utf8Match = contentDisposition.match(/filename\*=UTF-8''([^;]+)/i);
if (utf8Match?.[1]) {
try {
return decodeURIComponent(utf8Match[1]);
} catch {
return utf8Match[1];
}
}
const simpleMatch = contentDisposition.match(/filename="?([^"]+)"?/i);
return simpleMatch?.[1] || '';
};
/**
* 下载作品原图
* @param {number} workId - 作品ID
@@ -34,7 +52,8 @@ export const downloadWork = async (workId, fileName = null) => {
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName || `work_${workId}.jpg`;
const dispositionName = parseFilenameFromDisposition(response.headers?.['content-disposition']);
link.download = dispositionName || fileName || `work_${workId}`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);

View File

@@ -9,9 +9,47 @@ import { createPayment, queryPaymentStatus } from '../api/payment';
import { downloadWork } from '../api/download';
import { isLoggedIn } from '../api/auth';
import { API_CONFIG } from '../utils/config';
import Header from '../components/Header';
import Footer from '../components/Footer';
import './WorkDetail.css';
import Header from '../components/Header';
import Footer from '../components/Footer';
import './WorkDetail.css';
const parseTags = (rawTags) => {
if (!rawTags) {
return [];
}
if (Array.isArray(rawTags)) {
return rawTags.filter(Boolean);
}
if (typeof rawTags !== 'string') {
return [];
}
const trimmed = rawTags.trim();
if (!trimmed) {
return [];
}
if (trimmed.startsWith('[')) {
try {
const parsed = JSON.parse(trimmed);
return Array.isArray(parsed) ? parsed.filter(Boolean) : [];
} catch {
// 回退到逗号分隔解析,避免详情页白屏
}
}
return trimmed
.split(',')
.map((tag) => tag.trim())
.filter(Boolean);
};
const getDownloadFilename = (work) => {
const imagePath = String(work?.original_image || '');
const extMatch = imagePath.match(/\.[a-zA-Z0-9]+$/);
const ext = extMatch ? extMatch[0] : '.jpg';
return `${work?.title || 'work'}${ext}`;
};
const WorkDetail = () => {
const { id } = useParams();
@@ -97,7 +135,7 @@ const WorkDetail = () => {
}
// 尝试直接下载
const result = await downloadWork(id, `${workData.title}.jpg`);
const result = await downloadWork(id, getDownloadFilename(workData));
// 如果需要购买,显示购买确认弹窗
if (!result.success && result.needPurchase) {
@@ -148,7 +186,7 @@ const WorkDetail = () => {
setPaymentUrl('');
message.success('支付成功!开始下载...');
// 重新尝试下载
await downloadWork(id, `${workData.title}.jpg`);
await downloadWork(id, getDownloadFilename(workData));
}
}
}, 3000); // 每3秒检查一次
@@ -212,7 +250,7 @@ const WorkDetail = () => {
}
// 解析标签
const tags = workData.tags ? (typeof workData.tags === 'string' ? JSON.parse(workData.tags) : workData.tags) : [];
const tags = parseTags(workData.tags);
return (
<div className="work-detail-page">