feat(upload): remove image format conversion options #6

- Remove image format conversion and quality settings from the upload dialog
- Update backend to use default format and quality settings
- Add system configuration options for default image format and quality
This commit is contained in:
ShiYu
2025-05-24 11:41:06 +08:00
parent bec2c26b38
commit c2a9d07772
7 changed files with 116 additions and 114 deletions

View File

@@ -74,7 +74,6 @@ public class PictureController(IPictureService pictureService, IConfigService co
try
{
var userId = GetCurrentUserId();
await using var stream = request.File.OpenReadStream();
var result = await pictureService.UploadPictureAsync(
request.File.FileName,
@@ -83,9 +82,7 @@ public class PictureController(IPictureService pictureService, IConfigService co
userId,
(PermissionType)request.Permission!,
request.AlbumId,
request.StorageType,
request.ConvertToFormat,
request.Quality
request.StorageType
);
var picture = result.Picture;

View File

@@ -16,15 +16,4 @@ public record UploadPictureRequest
public int? AlbumId { get; set; }
public StorageType? StorageType { get; set; }
/// <summary>
/// 目标图片格式,默认为保持原格式
/// </summary>
public ImageFormat ConvertToFormat { get; set; } = ImageFormat.Original;
/// <summary>
/// 图片质量仅对JPEG和WebP有效1-100
/// </summary>
[Range(1, 100, ErrorMessage = "图片质量必须在1-100之间")]
public int Quality { get; set; } = 95;
}

View File

@@ -34,9 +34,7 @@ public interface IPictureService
int? userId,
PermissionType permission = PermissionType.Public,
int? albumId = null,
StorageType? storageType = null,
ImageFormat convertToFormat = ImageFormat.Original,
int quality = 95
StorageType? storageType = null
);
Task<ExifInfo> GetPictureExifInfoAsync(int pictureId);

View File

@@ -437,19 +437,17 @@ public class PictureService(
int? userId,
PermissionType permission = PermissionType.Public,
int? albumId = null,
StorageType? storageType = null,
ImageFormat convertToFormat = ImageFormat.Original,
int quality = 95)
StorageType? storageType = null)
{
StorageType GetConfigStorageType(string configKey)
{
string? configValue = configuration[configKey];
return !string.IsNullOrEmpty(configValue) &&
return !string.IsNullOrEmpty(configValue) &&
Enum.TryParse<StorageType>(configValue, out var configStorageType)
? configStorageType
? configStorageType
: StorageType.Local;
}
if (userId == null)
{
storageType = GetConfigStorageType("Storage:AnonymousDefaultStorage");
@@ -458,7 +456,21 @@ public class PictureService(
{
storageType = GetConfigStorageType("Storage:DefaultStorage");
}
ImageFormat convertToFormat = ImageFormat.Original;
string defaultFormatConfig = configuration["Upload:DefaultImageFormat"];
if (!string.IsNullOrEmpty(defaultFormatConfig))
{
if (Enum.TryParse<ImageFormat>(defaultFormatConfig, true, out var parsedFormat))
{
convertToFormat = parsedFormat;
}
}
int quality = 100;
string defaultQualityConfig = configuration["Upload:DefaultImageQuality"];
if (!string.IsNullOrEmpty(defaultQualityConfig))
{
quality = int.Parse(defaultQualityConfig);
}
string originalFileName = fileName;
string finalFileName = fileName;
string finalContentType = contentType;
@@ -553,7 +565,7 @@ public class PictureService(
AlbumId = albumId,
StorageType = storageType.Value,
ProcessingStatus = isAnonymous ? ProcessingStatus.Completed : ProcessingStatus.Pending,
ThumbnailPath = isAnonymous ? relativePath : null
ThumbnailPath = isAnonymous ? relativePath : null
};
dbContext.Pictures.Add(picture);
@@ -570,7 +582,7 @@ public class PictureService(
Id = picture.Id,
Name = picture.Name,
Path = storageService.GetUrl(picture.StorageType, relativePath),
ThumbnailPath = isAnonymous ? storageService.GetUrl(picture.StorageType, relativePath) : null,
ThumbnailPath = isAnonymous ? storageService.GetUrl(picture.StorageType, relativePath) : null,
Description = picture.Description,
CreatedAt = picture.CreatedAt,
Tags = new List<string>(),
@@ -590,7 +602,7 @@ public class PictureService(
string tempFilePath = tempFileStream.Name;
finalStream.Dispose();
if (File.Exists(tempFilePath)) File.Delete(tempFilePath);
// 同时清理原始临时文件
string tempOriginalFile = Path.ChangeExtension(tempFilePath, null);
if (File.Exists(tempOriginalFile)) File.Delete(tempOriginalFile);

View File

@@ -119,8 +119,6 @@ export async function uploadPicture(
data: {
permission?: number;
albumId?: number;
convertToFormat?: string;
quality?: number;
onProgress?: (percent: number) => void
} = {}
): Promise<BaseResult<PictureResponse>> {
@@ -135,14 +133,6 @@ export async function uploadPicture(
formData.append('albumId', data.albumId.toString());
}
if (data.convertToFormat !== undefined) {
formData.append('convertToFormat', data.convertToFormat.toString());
}
if (data.quality !== undefined) {
formData.append('quality', data.quality.toString());
}
try {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {};

View File

@@ -1,9 +1,9 @@
import React, { useState, useEffect } from 'react';
import { Modal, Upload, Button, Progress, message, Form, Select, Radio, Slider, Divider, Alert } from 'antd';
import { Modal, Upload, Button, Progress, message, Form, Select, Radio, Slider } from 'antd';
import { InboxOutlined } from '@ant-design/icons';
import { v4 as uuidv4 } from 'uuid';
import type { UploadFile, AlbumResponse } from '../../api';
import { uploadPicture, getAlbums, ImageFormat } from '../../api';
import { uploadPicture, getAlbums } from '../../api';
const { Dragger } = Upload;
const { Option } = Select;
@@ -20,8 +20,6 @@ const ImageUploadDialog: React.FC<UploadDialogProps> = ({ visible, onClose, onUp
const [form] = Form.useForm();
const [albums, setAlbums] = useState<AlbumResponse[]>([]);
const [concurrentUploads, setConcurrentUploads] = useState<number>(3);
const [convertFormat, setConvertFormat] = useState<ImageFormat>(ImageFormat.Original);
const [quality, setQuality] = useState<number>(95);
useEffect(() => {
if (visible) {
@@ -77,11 +75,9 @@ const ImageUploadDialog: React.FC<UploadDialogProps> = ({ visible, onClose, onUp
setUploading(true);
const values = await form.validateFields();
const params: { // 修改此处的类型定义
const params: {
permission?: number;
albumId?: number;
convertToFormat?: string; // 允许 string 类型
quality?: number;
} = {};
if (values.permission !== undefined) {
@@ -90,10 +86,6 @@ const ImageUploadDialog: React.FC<UploadDialogProps> = ({ visible, onClose, onUp
if (values.albumId) {
params.albumId = values.albumId;
}
if (convertFormat !== ImageFormat.Original) {
params.convertToFormat = convertFormat.toString(); // 现在类型匹配
params.quality = quality;
}
let successCount = 0;
let failCount = 0;
@@ -110,7 +102,7 @@ const ImageUploadDialog: React.FC<UploadDialogProps> = ({ visible, onClose, onUp
try {
// 上传文件
const result = await uploadPicture(item.file, { // 此处 params 类型现在匹配
const result = await uploadPicture(item.file, {
...params,
onProgress: (percent) => {
setUploadQueue((prev) =>
@@ -217,17 +209,6 @@ const ImageUploadDialog: React.FC<UploadDialogProps> = ({ visible, onClose, onUp
}
};
// 获取格式名称
const getFormatName = (format: ImageFormat) => {
switch (format) {
case ImageFormat.Original: return '保持原格式';
case ImageFormat.Jpeg: return 'JPEG';
case ImageFormat.Png: return 'PNG';
case ImageFormat.WebP: return 'WebP';
default: return '未知格式';
}
};
// 自定义上传列表项
const renderUploadItem = (item: UploadFile) => {
let statusIcon;
@@ -361,51 +342,6 @@ const ImageUploadDialog: React.FC<UploadDialogProps> = ({ visible, onClose, onUp
<Radio value={2}></Radio>
</Radio.Group>
</Form.Item>
<Divider orientation="left"></Divider>
<Form.Item label="输出格式">
<Radio.Group
value={convertFormat}
onChange={(e) => setConvertFormat(e.target.value)}
optionType="button"
buttonStyle="solid"
style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}
>
<Radio.Button value={ImageFormat.Original}></Radio.Button>
<Radio.Button value={ImageFormat.Jpeg}>JPEG (.jpg)</Radio.Button>
<Radio.Button value={ImageFormat.Png}>PNG (.png)</Radio.Button>
<Radio.Button value={ImageFormat.WebP}>WebP (.webp)</Radio.Button>
</Radio.Group>
</Form.Item>
{convertFormat !== ImageFormat.Original && (
<>
{convertFormat === ImageFormat.Png ? (
<Alert
message="提示"
description={`${getFormatName(convertFormat)} 格式为无损压缩,不支持质量调节。`}
type="info"
showIcon
style={{ marginBottom: '16px' }}
/>
) : (
<Form.Item label={`图片质量 (${quality}%)`}>
<Slider
min={50}
max={100}
value={quality}
onChange={setQuality}
marks={{ 50: '50%', 75: '75%', 90: '90%', 95: '95%', 100: '100%' }}
tooltip={{ formatter: (value) => `${value}%` }}
/>
<div style={{ fontSize: '12px', color: '#666', marginTop: '4px' }}>
使 85-95%
</div>
</Form.Item>
)}
</>
)}
<Form.Item
name="concurrentUploads"
@@ -434,14 +370,10 @@ const ImageUploadDialog: React.FC<UploadDialogProps> = ({ visible, onClose, onUp
<p className="ant-upload-text"></p>
<p className="ant-upload-hint">
10MB
{convertFormat !== ImageFormat.Original && (
<>
<br />
<span style={{ color: '#1890ff' }}>
{getFormatName(convertFormat)}
</span>
</>
)}
<br />
<span style={{ color: '#888' }}>
</span>
</p>
</Dragger>

View File

@@ -391,6 +391,90 @@ const SystemConfig: React.FC = () => {
</div>
</Card>
{/* 上传设置卡片 - 新增 */}
<Card
size="small"
title="上传设置配置"
style={{ marginBottom: isMobile ? 16 : 24 }}
bodyStyle={{ padding: isMobile ? '12px' : '16px' }}
>
<div style={{
display: 'grid',
gridTemplateColumns: isMobile ? '1fr' : 'repeat(auto-fit, minmax(300px, 1fr))',
gap: isMobile ? 12 : 16,
marginBottom: 0
}}>
{/* 图片默认格式 */}
<div>
<div style={{
marginBottom: 8,
fontSize: 14,
fontWeight: 500,
color: '#666'
}}>
</div>
<Select
value={configs.Upload?.DefaultImageFormat || 'Original'}
onChange={(value) => {
handleSaveConfig('Upload', 'DefaultImageFormat', value);
}}
style={{ width: '100%' }}
size="large"
placeholder="选择上传图片的默认处理格式"
>
<Option value="Original"></Option>
<Option value="Jpeg">JPEG</Option>
<Option value="Png">PNG</Option>
<Option value="Webp">WebP</Option>
</Select>
<div style={{
fontSize: 12,
color: '#999',
marginTop: 4
}}>
</div>
</div>
{/* 图片压缩质量 */}
<div>
<div style={{
marginBottom: 8,
fontSize: 14,
fontWeight: 500,
color: '#666'
}}>
</div>
<Select
value={configs.Upload?.DefaultImageQuality || '95'}
onChange={(value) => {
handleSaveConfig('Upload', 'DefaultImageQuality', value);
}}
style={{ width: '100%' }}
size="large"
placeholder="选择图片压缩质量"
>
<Option value="100">100% - </Option>
<Option value="95">95% - </Option>
<Option value="90">90% - </Option>
<Option value="85">85% - </Option>
<Option value="80">80% - </Option>
<Option value="75">75% - </Option>
<Option value="70">70% - </Option>
</Select>
<div style={{
fontSize: 12,
color: '#999',
marginTop: 4
}}>
JPEG和WebP格式的图片质量设置
</div>
</div>
</div>
</Card>
{/* 存储服务配置卡片 */}
<Card
size="small"