添加备用变清晰API (Real-ESRGAN)

功能:
- 在系统设置中添加备用API配置选项
- 支持管理员切换使用主API或备用Real-ESRGAN API
- 备用API使用Base64方式发送图片
- 支持配置放大倍数(2x/4x/8x)和模型选择

Real-ESRGAN API特点:
- 基于深度学习的图像超分辨率模型
- 支持RealESRGAN_x2plus/x4plus/x8plus模型
- 支持动漫视频模型
- 处理时间1-10秒

后台切换对前端用户透明无感知
This commit is contained in:
Your Name 2026-01-04 15:50:38 +08:00
parent a473d9710c
commit 88ac9e2a46
5 changed files with 1387 additions and 97 deletions

902
Real-ESRGAN_API_文档.md Normal file
View File

@ -0,0 +1,902 @@
# Real-ESRGAN API 接口文档
## 服务信息
- **服务地址**: http://156.225.27.52:5233(通过frpc穿透)
- **本地端口**: 8002(仅服务器内部访问)
- **远程端口**: 5233(外部访问端口)
- **API版本**: 1.0.0
- **模型**: Real-ESRGAN_x4plus(图像超分辨率模型)
- **框架**: FastAPI
- **API文档**: http://156.225.27.52:5233/docs
- **OpenAPI规范**: http://156.225.27.52:5233/openapi.json
- **API密钥**: 所有核心接口需要API密钥认证(详见下方认证说明)
---
## 认证方式
所有核心接口(`/upscale/file`、`/upscale/base64` 和 `/upscale/url`)需要在请求头中添加API密钥:
**请求头**:
```
X-API-Key: realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw
```
**注意**: 以下端点无需API密钥:
- `GET /` - 根端点(服务状态检查)
- `GET /health` - 健康检查
- `GET /api-key-info` - API密钥信息
- `GET /models` - 支持的模型列表
---
## 接口列表
### 1. 根端点 - 服务状态检查
**接口地址**: `GET /`
**描述**: 检查服务是否正常运行
**请求示例**:
```bash
curl http://156.225.27.52:5233/
```
**响应示例**:
```json
{
"message": "Real-ESRGAN API 服务运行正常",
"version": "1.0.0",
"model": "RealESRGAN_x4plus",
"scale": 4
}
```
---
### 2. 健康检查
**接口地址**: `GET /health`
**描述**: 检查服务健康状态和模型加载状态(无需API密钥)
**请求示例**:
```bash
curl http://156.225.27.52:5233/health
```
**响应示例**:
```json
{
"status": "healthy",
"model_loaded": true,
"model": "RealESRGAN_x4plus",
"scale": 4,
"device": "cuda"
}
```
---
### 3. API密钥信息
**接口地址**: `GET /api-key-info`
**描述**: 获取API密钥使用说明(无需API密钥)
**请求示例**:
```bash
curl http://156.225.27.52:5233/api-key-info
```
**响应示例**:
```json
{
"api_key_name": "X-API-Key",
"description": "请在请求头中添加API密钥"
}
```
---
### 4. 支持的模型列表
**接口地址**: `GET /models`
**描述**: 获取所有支持的模型列表(无需API密钥)
**请求示例**:
```bash
curl http://156.225.27.52:5233/models
```
**响应示例**:
```json
{
"models": [
{
"name": "RealESRGAN_x2plus",
"scale": 2,
"description": "2倍放大模型"
},
{
"name": "RealESRGAN_x4plus",
"scale": 4,
"description": "4倍放大模型(推荐)"
},
{
"name": "RealESRGAN_x8plus",
"scale": 8,
"description": "8倍放大模型"
},
{
"name": "RealESRGAN_animevideov3",
"scale": 4,
"description": "动漫视频模型"
}
]
}
```
---
### 5. 文件上传方式放大图像
**接口地址**: `POST /upscale/file`
**描述**: 通过文件上传方式对图片进行超分辨率处理(需要API密钥)
**Content-Type**: `multipart/form-data`
**请求头**:
```
X-API-Key: realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw
```
**请求参数**:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| file | File | 是 | - | 图片文件(支持常见图片格式) |
| scale | int | 否 | 4 | 放大倍数(2, 4, 8) |
| model_name | string | 否 | RealESRGAN_x4plus | 模型名称 |
| tile | int | 否 | 0 | 分块大小(0=不使用分块,可根据显存调整) |
| half | bool | 否 | false | 是否使用FP16(降低显存需求) |
**请求示例 (cURL)**:
```bash
curl -X POST http://156.225.27.52:5233/upscale/file \
-H "X-API-Key: realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw" \
-F "file=@/path/to/your/image.jpg" \
-F "scale=4" \
-F "model_name=RealESRGAN_x4plus"
```
**请求示例 (Python)**:
```python
import requests
url = "http://156.225.27.52:5233/upscale/file"
headers = {
"X-API-Key": "realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw"
}
files = {"file": open("image.jpg", "rb")}
params = {
"scale": 4,
"model_name": "RealESRGAN_x4plus",
"tile": 0,
"half": False
}
response = requests.post(url, files=files, headers=headers, params=params)
# 保存结果
with open("result.png", "wb") as f:
f.write(response.content)
print(f"处理时间: {response.headers.get('X-Processing-Time')} 秒")
print(f"放大倍数: {response.headers.get('X-Scale')}x")
print(f"使用模型: {response.headers.get('X-Model')}")
```
**请求示例 (JavaScript/Node.js)**:
```javascript
const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');
const form = new FormData();
form.append('file', fs.createReadStream('image.jpg'));
form.append('scale', '4');
form.append('model_name', 'RealESRGAN_x4plus');
axios.post('http://156.225.27.52:5233/upscale/file', form, {
headers: {
...form.getHeaders(),
'X-API-Key': 'realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw'
},
responseType: 'arraybuffer'
})
.then(response => {
fs.writeFileSync('result.png', response.data);
console.log('处理时间:', response.headers['x-processing-time'], '秒');
console.log('放大倍数:', response.headers['x-scale'], 'x');
});
```
**响应**:
- **Content-Type**: `image/png`
- **Body**: PNG格式的图片二进制数据(超分辨率处理后的图片)
- **Headers**:
- `X-Processing-Time`: 处理时间(秒)
- `X-Scale`: 放大倍数
- `X-Model`: 使用的模型名称
**错误响应**:
```json
{
"detail": "Invalid API key"
}
```
(当API密钥无效或缺失时)
---
### 6. Base64方式放大图像
**接口地址**: `POST /upscale/base64`
**描述**: 通过Base64编码方式对图片进行超分辨率处理(需要API密钥)
**Content-Type**: `application/json`
**请求头**:
```
X-API-Key: realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw
```
**请求体**:
```json
{
"image_base64": "base64编码的图片字符串",
"scale": 4,
"model_name": "RealESRGAN_x4plus"
}
```
**请求参数**:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| image_base64 | string | 是 | - | Base64编码的图片数据 |
| scale | int | 否 | 4 | 放大倍数(2, 4, 8) |
| model_name | string | 否 | RealESRGAN_x4plus | 模型名称 |
| tile | int | 否 | 0 | 分块大小(Query参数) |
| half | bool | 否 | false | 是否使用FP16(Query参数) |
**请求示例 (cURL)**:
```bash
curl -X POST http://156.225.27.52:5233/upscale/base64 \
-H "X-API-Key: realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw" \
-H "Content-Type: application/json" \
-d '{
"image_base64": "iVBORw0KGgoAAAANSUhEUgAA...",
"scale": 4,
"model_name": "RealESRGAN_x4plus"
}'
```
**请求示例 (Python)**:
```python
import requests
import base64
# 读取图片并转换为base64
with open("image.jpg", "rb") as f:
image_base64 = base64.b64encode(f.read()).decode("utf-8")
url = "http://156.225.27.52:5233/upscale/base64"
headers = {
"X-API-Key": "realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw",
"Content-Type": "application/json"
}
data = {
"image_base64": image_base64,
"scale": 4,
"model_name": "RealESRGAN_x4plus"
}
params = {
"tile": 0,
"half": False
}
response = requests.post(url, json=data, headers=headers, params=params)
if response.status_code == 200:
result = response.json()
# 保存结果
result_image = base64.b64decode(result["image_base64"])
with open("result.png", "wb") as f:
f.write(result_image)
print(f"✅ 成功! 处理时间: {result['processing_time']:.2f}秒")
```
**请求示例 (JavaScript/Node.js)**:
```javascript
const fs = require('fs');
const axios = require('axios');
// 读取图片并转换为base64
const imageBuffer = fs.readFileSync('image.jpg');
const imageBase64 = imageBuffer.toString('base64');
axios.post('http://156.225.27.52:5233/upscale/base64', {
image_base64: imageBase64,
scale: 4,
model_name: 'RealESRGAN_x4plus'
}, {
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw'
},
params: {
tile: 0,
half: false
}
})
.then(response => {
if (response.data.success) {
const resultImage = Buffer.from(response.data.image_base64, 'base64');
fs.writeFileSync('result.png', resultImage);
console.log('处理时间:', response.data.processing_time, '秒');
}
});
```
**响应示例**:
```json
{
"success": true,
"image_base64": "iVBORw0KGgoAAAANSUhEUgAA...",
"processing_time": 2.345,
"scale": 4,
"model": "RealESRGAN_x4plus"
}
```
**响应参数**:
| 参数名 | 类型 | 说明 |
|--------|------|------|
| success | boolean | 是否成功 |
| image_base64 | string | Base64编码的结果图片(PNG格式) |
| processing_time | number | 处理时间(秒) |
| scale | int | 放大倍数 |
| model | string | 使用的模型名称 |
---
### 7. URL方式放大图像(上传到S3)
**接口地址**: `POST /upscale/url`
**描述**: 通过图片URL对图片进行超分辨率处理,处理后的图片自动上传到S3存储桶并返回URL(需要API密钥)
**Content-Type**: `application/json`
**请求头**:
```
X-API-Key: realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw
```
**请求体**:
```json
{
"image_url": "https://example.com/image.jpg",
"scale": 4,
"model_name": "RealESRGAN_x4plus"
}
```
**请求参数**:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|--------|------|------|--------|------|
| image_url | string | 是 | - | 图片的URL地址 |
| scale | int | 否 | 4 | 放大倍数(2, 4, 8) |
| model_name | string | 否 | RealESRGAN_x4plus | 模型名称 |
| tile | int | 否 | 0 | 分块大小(Query参数) |
| half | bool | 否 | false | 是否使用FP16(Query参数) |
**请求示例 (cURL)**:
```bash
curl -X POST http://156.225.27.52:5233/upscale/url \
-H "X-API-Key: realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://example.com/image.jpg",
"scale": 4,
"model_name": "RealESRGAN_x4plus"
}'
```
**请求示例 (Python)**:
```python
import requests
url = "http://156.225.27.52:5233/upscale/url"
headers = {
"X-API-Key": "realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw",
"Content-Type": "application/json"
}
data = {
"image_url": "https://example.com/image.jpg",
"scale": 4,
"model_name": "RealESRGAN_x4plus"
}
params = {
"tile": 0,
"half": False
}
response = requests.post(url, json=data, headers=headers, params=params)
if response.status_code == 200:
result = response.json()
print(f"✅ 成功!")
print(f"处理后的图片URL: {result['image_url']}")
print(f"下载时间: {result['download_time']:.2f}秒")
print(f"处理时间: {result['processing_time']:.2f}秒")
print(f"上传时间: {result['upload_time']:.2f}秒")
print(f"总时间: {result['total_time']:.2f}秒")
```
**请求示例 (JavaScript/Node.js)**:
```javascript
const axios = require('axios');
axios.post('http://156.225.27.52:5233/upscale/url', {
image_url: 'https://example.com/image.jpg',
scale: 4,
model_name: 'RealESRGAN_x4plus'
}, {
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw'
},
params: {
tile: 0,
half: false
}
})
.then(response => {
if (response.data.success) {
console.log('✅ 成功!');
console.log('处理后的图片URL:', response.data.image_url);
console.log('下载时间:', response.data.download_time, '秒');
console.log('处理时间:', response.data.processing_time, '秒');
console.log('上传时间:', response.data.upload_time, '秒');
console.log('总时间:', response.data.total_time, '秒');
}
})
.catch(error => {
console.error('❌ 失败:', error.response?.data || error.message);
});
```
**响应示例**:
```json
{
"success": true,
"image_url": "https://s3.1080p.hk/jsxiangche/realesrgan/20260104/abc123def456.png",
"processing_time": 2.345,
"download_time": 0.856,
"upload_time": 0.234,
"total_time": 3.435,
"scale": 4,
"model": "RealESRGAN_x4plus"
}
```
**响应参数**:
| 参数名 | 类型 | 说明 |
|--------|------|------|
| success | boolean | 是否成功 |
| image_url | string | 处理后的图片在S3中的URL地址 |
| processing_time | number | 图片处理时间(秒) |
| download_time | number | 图片下载时间(秒) |
| upload_time | number | 图片上传到S3的时间(秒) |
| total_time | number | 总耗时(秒) |
| scale | int | 放大倍数 |
| model | string | 使用的模型名称 |
**注意事项**:
- 此接口需要S3存储功能已启用(`USE_S3_STORAGE=true`)
- 图片URL必须可公开访问
- 支持的图片格式:JPG, PNG, JPEG等常见格式
- 图片大小限制:最大50MB
- 处理后的图片会自动上传到S3存储桶并返回可访问的URL
---
## 服务管理
Real-ESRGAN API服务通过systemd管理,支持自动启动和崩溃自动重启。
### 服务状态检查
```bash
# 查看服务状态
systemctl status realesrgan-api.service
# 查看服务日志
journalctl -u realesrgan-api.service -f
# 查看最近100行日志
journalctl -u realesrgan-api.service -n 100
```
### 服务控制命令
```bash
# 启动服务
systemctl start realesrgan-api.service
# 停止服务
systemctl stop realesrgan-api.service
# 重启服务
systemctl restart realesrgan-api.service
# 重新加载配置(无需重启)
systemctl reload realesrgan-api.service
# 查看服务状态
systemctl status realesrgan-api.service
```
### 自动启动配置
服务已配置为系统启动时自动启动,崩溃后自动重启:
- **自动启动**: 服务已启用(`systemctl enable realesrgan-api.service`)
- **崩溃重启**: `Restart=always`,服务崩溃后自动重启
- **重启延迟**: `RestartSec=10`,崩溃后等待10秒再重启
- **重启策略**: 服务异常退出时会自动重启,无需手动干预
### 服务配置
服务配置文件位置:`/etc/systemd/system/realesrgan-api.service`
主要配置项:
- **工作目录**: `/root/realesrgan-api`
- **运行用户**: `root`
- **GPU设备**: `CUDA_VISIBLE_DEVICES=1`(使用GPU1)
- **S3存储**: 已启用,环境变量已配置
- **日志输出**: 通过systemd journal管理
### 日志查看
```bash
# 实时查看日志
journalctl -u realesrgan-api.service -f
# 查看今天的日志
journalctl -u realesrgan-api.service --since today
# 查看最近的错误日志
journalctl -u realesrgan-api.service -p err -n 50
```
---
## 错误码说明
| HTTP状态码 | 说明 |
|-----------|------|
| 200 | 请求成功 |
| 400 | 请求参数错误(如文件格式不支持、图片URL无效等) |
| 401 | 未授权(API密钥无效或缺失) |
| 422 | 请求参数验证失败 |
| 500 | 服务器内部错误(如处理失败、S3上传失败等) |
---
## 注意事项
1. **服务地址**:
- 外部访问地址:http://156.225.27.52:5233(通过frpc穿透)
- 本地端口:8002(仅服务器内部访问)
- 远程端口:5233(外部访问端口)
2. **API密钥认证**:
- 所有核心接口(`/upscale/file`、`/upscale/base64` 和 `/upscale/url`)都需要在请求头中添加 `X-API-Key`
- API密钥:`realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw`
- 健康检查、模型列表、API密钥信息等端点无需密钥
3. **支持的图片格式**: 所有常见图片格式(JPG, PNG, JPEG等)
4. **输出格式**: 所有接口返回PNG格式的图片
5. **文件大小限制**:
- 上传文件:建议不超过50MB
- URL方式:图片下载大小限制50MB
6. **处理时间**: 处理时间取决于图片大小和服务器负载,通常在1-10秒之间
7. **放大倍数**: 支持2x、4x、8x放大,根据模型而定
8. **模型选择**:
- `RealESRGAN_x4plus`:推荐,4倍放大,适合大多数场景
- `RealESRGAN_x2plus`:2倍放大,处理速度快
- `RealESRGAN_x8plus`:8倍放大,需要更多显存
- `RealESRGAN_animevideov3`:动漫视频模型,4倍放大
9. **S3存储功能**:
- `/upscale/url` 接口需要S3存储功能启用
- S3配置可通过环境变量设置
- 处理后的图片会自动上传到S3并返回可访问的URL
10. **CORS**: 服务已配置CORS,支持跨域请求
11. **服务管理**:
- 服务通过systemd管理,服务名称:`realesrgan-api.service`
- 系统重启后服务会自动启动(已启用)
- 服务崩溃后会在10秒内自动重启(`Restart=always`)
- 日志通过systemd journal管理,使用 `journalctl -u realesrgan-api.service` 查看
- 服务控制命令:`systemctl start/stop/restart/status realesrgan-api.service`
---
## 完整使用示例
### Python完整示例
```python
import requests
import base64
from pathlib import Path
API_KEY = "realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw"
BASE_URL = "http://156.225.27.52:5233"
# 方法1: 文件上传
def upscale_file(image_path):
url = f"{BASE_URL}/upscale/file"
headers = {"X-API-Key": API_KEY}
with open(image_path, "rb") as f:
files = {"file": f}
params = {"scale": 4, "model_name": "RealESRGAN_x4plus"}
response = requests.post(url, files=files, headers=headers, params=params)
if response.status_code == 200:
output_path = Path(image_path).stem + "_upscaled.png"
with open(output_path, "wb") as f:
f.write(response.content)
print(f"✅ 成功! 结果已保存到: {output_path}")
return output_path
else:
print(f"❌ 失败: {response.json()}")
return None
# 方法2: Base64
def upscale_base64(image_path):
url = f"{BASE_URL}/upscale/base64"
headers = {
"X-API-Key": API_KEY,
"Content-Type": "application/json"
}
with open(image_path, "rb") as f:
image_base64 = base64.b64encode(f.read()).decode("utf-8")
data = {
"image_base64": image_base64,
"scale": 4,
"model_name": "RealESRGAN_x4plus"
}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
result = response.json()
result_image = base64.b64decode(result["image_base64"])
output_path = Path(image_path).stem + "_upscaled.png"
with open(output_path, "wb") as f:
f.write(result_image)
print(f"✅ 成功! 处理时间: {result['processing_time']:.2f}秒")
return output_path
else:
print(f"❌ 失败: {response.json()}")
return None
# 方法3: URL方式(上传到S3)
def upscale_url(image_url):
url = f"{BASE_URL}/upscale/url"
headers = {
"X-API-Key": API_KEY,
"Content-Type": "application/json"
}
data = {
"image_url": image_url,
"scale": 4,
"model_name": "RealESRGAN_x4plus"
}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
result = response.json()
print(f"✅ 成功!")
print(f"处理后的图片URL: {result['image_url']}")
print(f"总时间: {result['total_time']:.2f}秒")
return result["image_url"]
else:
print(f"❌ 失败: {response.json()}")
return None
# 使用示例
if __name__ == "__main__":
image_path = "test_image.jpg"
# 使用文件上传方式
upscale_file(image_path)
# 或使用Base64方式
# upscale_base64(image_path)
# 或使用URL方式
# upscale_url("https://example.com/image.jpg")
```
### JavaScript/Node.js完整示例
```javascript
const FormData = require('form-data');
const fs = require('fs');
const axios = require('axios');
const path = require('path');
const API_KEY = 'realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw';
const BASE_URL = 'http://156.225.27.52:5233';
// 方法1: 文件上传
async function upscaleFile(imagePath) {
const form = new FormData();
form.append('file', fs.createReadStream(imagePath));
form.append('scale', '4');
form.append('model_name', 'RealESRGAN_x4plus');
try {
const response = await axios.post(`${BASE_URL}/upscale/file`, form, {
headers: {
...form.getHeaders(),
'X-API-Key': API_KEY
},
responseType: 'arraybuffer'
});
const outputPath = path.parse(imagePath).name + '_upscaled.png';
fs.writeFileSync(outputPath, response.data);
console.log(`✅ 成功! 结果已保存到: ${outputPath}`);
return outputPath;
} catch (error) {
console.error('❌ 失败:', error.response?.data || error.message);
return null;
}
}
// 方法2: Base64
async function upscaleBase64(imagePath) {
const imageBuffer = fs.readFileSync(imagePath);
const imageBase64 = imageBuffer.toString('base64');
try {
const response = await axios.post(`${BASE_URL}/upscale/base64`, {
image_base64: imageBase64,
scale: 4,
model_name: 'RealESRGAN_x4plus'
}, {
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
}
});
if (response.data.success) {
const outputPath = path.parse(imagePath).name + '_upscaled.png';
const resultImage = Buffer.from(response.data.image_base64, 'base64');
fs.writeFileSync(outputPath, resultImage);
console.log(`✅ 成功! 处理时间: ${response.data.processing_time.toFixed(2)}秒`);
return outputPath;
}
} catch (error) {
console.error('❌ 失败:', error.response?.data || error.message);
return null;
}
}
// 方法3: URL方式(上传到S3)
async function upscaleUrl(imageUrl) {
try {
const response = await axios.post(`${BASE_URL}/upscale/url`, {
image_url: imageUrl,
scale: 4,
model_name: 'RealESRGAN_x4plus'
}, {
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
}
});
if (response.data.success) {
console.log('✅ 成功!');
console.log('处理后的图片URL:', response.data.image_url);
console.log('总时间:', response.data.total_time.toFixed(2), '秒');
return response.data.image_url;
}
} catch (error) {
console.error('❌ 失败:', error.response?.data || error.message);
return null;
}
}
// 使用示例
(async () => {
const imagePath = 'test_image.jpg';
// 使用文件上传方式
await upscaleFile(imagePath);
// 或使用Base64方式
// await upscaleBase64(imagePath);
// 或使用URL方式
// await upscaleUrl('https://example.com/image.jpg');
})();
```
---
## 交互式API文档
访问 http://156.225.27.52:5233/docs 可以在浏览器中查看交互式API文档(Swagger UI),支持在线测试接口。
**注意**: 在Swagger UI中测试需要API密钥的接口时,需要先点击右上角的"Authorize"按钮(如果有),或者直接在请求头中添加 `X-API-Key`。
---
## API密钥
当前API密钥:
```
realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw
```
**请求头格式**:
```
X-API-Key: realesrgan_sk_live_MAPF4kLW5QinFz5BO96w-oVk09WnmvBZSyNDG8AxUgo7HGlqssJ4y1XZ38SyQsG9QCsYYOLcp9df6Ku1Jw78Pw
```
**安全提示**:
- 请妥善保管API密钥,不要将其暴露在客户端代码中
- 建议通过环境变量或配置文件管理API密钥
---
## S3存储配置
如果使用 `/upscale/url` 接口,需要配置S3存储。配置方式如下:
**环境变量**:
```bash
USE_S3_STORAGE=true
MINIO_S3_ENDPOINT=s3.1080p.hk
MINIO_S3_USE_SSL=true
MINIO_S3_BUCKET=jsxiangche
MINIO_S3_ACCESS_KEY=AEvT1p0wSNjow3Asu9NV
MINIO_S3_SECRET_KEY=u8nzFl1S6dNoT5PQaQhkHz8Du6iX0hVnPswtvHhs
MINIO_S3_REGION=us-east-1
```
**说明**:
- `USE_S3_STORAGE`: 必须设置为 `true` 才能启用S3上传功能
- 处理后的图片会保存到S3存储桶中,路径格式:`realesrgan/YYYYMMDD/UUID.png`
- 返回的URL可以直接访问和下载

View File

@ -355,6 +355,12 @@ export function AdminSettingsClient() {
apiKey: "",
type: "clean", // "clean" | "face"
scaleFactor: "auto", // "auto" | "1" | "2" | "4"
// 备用API配置 (Real-ESRGAN)
useBackupApi: false, // 是否启用备用API
backupApiUrl: "http://156.225.27.52:5233", // 备用API地址
backupApiKey: "", // 备用API密钥
backupScale: "4", // 备用API放大倍数 "2" | "4" | "8"
backupModel: "RealESRGAN_x4plus", // 备用API模型
});
// 图像拓展API配置状态
@ -385,12 +391,22 @@ export function AdminSettingsClient() {
apiKey?: string;
type?: string;
scaleFactor?: number;
useBackupApi?: boolean;
backupApiUrl?: string;
backupApiKey?: string;
backupScale?: string;
backupModel?: string;
};
setEnhanceConfig({
apiUrl: config.apiUrl || "",
apiKey: config.apiKey || "",
type: config.type || "clean",
scaleFactor: config.scaleFactor ? String(config.scaleFactor) : "auto",
useBackupApi: config.useBackupApi || false,
backupApiUrl: config.backupApiUrl || "http://156.225.27.52:5233",
backupApiKey: config.backupApiKey || "",
backupScale: config.backupScale || "4",
backupModel: config.backupModel || "RealESRGAN_x4plus",
});
}
}, [enhanceConfigData]);
@ -722,14 +738,26 @@ export function AdminSettingsClient() {
// --- 变清晰API配置逻辑 ---
const handleSaveEnhanceConfig = async () => {
try {
if (!enhanceConfig.apiUrl.trim()) {
toast.error("请输入API地址");
return;
}
if (!enhanceConfig.apiKey.trim()) {
toast.error("请输入API密钥");
return;
// 如果启用了备用API,验证备用API配置
if (enhanceConfig.useBackupApi) {
if (!enhanceConfig.backupApiUrl.trim()) {
toast.error("请输入备用API地址");
return;
}
if (!enhanceConfig.backupApiKey.trim()) {
toast.error("请输入备用API密钥");
return;
}
} else {
// 如果使用主API,验证主API配置
if (!enhanceConfig.apiUrl.trim()) {
toast.error("请输入API地址");
return;
}
if (!enhanceConfig.apiKey.trim()) {
toast.error("请输入API密钥");
return;
}
}
const response = await fetch("/api/admin/settings", {
@ -742,6 +770,12 @@ export function AdminSettingsClient() {
apiKey: enhanceConfig.apiKey.trim(),
type: enhanceConfig.type,
scaleFactor: enhanceConfig.scaleFactor && enhanceConfig.scaleFactor !== "auto" ? Number.parseInt(enhanceConfig.scaleFactor) : undefined,
// 备用API配置
useBackupApi: enhanceConfig.useBackupApi,
backupApiUrl: enhanceConfig.backupApiUrl.trim(),
backupApiKey: enhanceConfig.backupApiKey.trim(),
backupScale: enhanceConfig.backupScale,
backupModel: enhanceConfig.backupModel,
},
}),
});
@ -3085,83 +3119,190 @@ export function AdminSettingsClient() {
<CardHeader>
<CardTitle>变清晰API配置</CardTitle>
<CardDescription>
配置图片变清晰API,用于画图标注功能中的图片增强处理
配置图片变清晰API,用于画图标注功能中的图片增强处理。支持主API和备用API切换。
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 pt-6">
<div className="space-y-2">
<Label htmlFor="enhanceApiUrl">API地址 *</Label>
<Input
id="enhanceApiUrl"
value={enhanceConfig.apiUrl}
onChange={(e) => setEnhanceConfig({ ...enhanceConfig, apiUrl: e.target.value })}
placeholder="https://techsz.aoscdn.com"
{/* API切换开关 */}
<div className="flex items-center justify-between rounded-lg border p-4">
<div className="space-y-0.5">
<Label className="text-base">使用备用API (Real-ESRGAN)</Label>
<div className="text-sm text-muted-foreground">
启用后将使用Real-ESRGAN本地模型进行图片增强,效果更好
</div>
</div>
<Switch
checked={enhanceConfig.useBackupApi}
onCheckedChange={(checked) => setEnhanceConfig({ ...enhanceConfig, useBackupApi: checked })}
/>
<div className="text-xs text-muted-foreground">
请输入完整的API地址,例如: https://techsz.aoscdn.com
</div>
</div>
<div className="space-y-2">
<Label htmlFor="enhanceApiKey">API密钥 *</Label>
<Input
id="enhanceApiKey"
type="password"
value={enhanceConfig.apiKey}
onChange={(e) => setEnhanceConfig({ ...enhanceConfig, apiKey: e.target.value })}
placeholder="请输入API密钥"
/>
<div className="text-xs text-muted-foreground">
API密钥是必需的,请从服务提供商获取
</div>
</div>
<div className="space-y-2">
<Label htmlFor="enhanceType">修复类型</Label>
<Select
value={enhanceConfig.type}
onValueChange={(value) => setEnhanceConfig({ ...enhanceConfig, type: value })}
>
<SelectTrigger id="enhanceType">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="clean">通用放大清晰化</SelectItem>
<SelectItem value="face">人像放大清晰化</SelectItem>
</SelectContent>
</Select>
<div className="text-xs text-muted-foreground">
选择图片类型:通用或人像(人像模式最多同时处理10个人)
</div>
</div>
<div className="space-y-2">
<Label htmlFor="enhanceScaleFactor">放大倍数(可选)</Label>
<Select
value={enhanceConfig.scaleFactor}
onValueChange={(value) => setEnhanceConfig({ ...enhanceConfig, scaleFactor: value })}
>
<SelectTrigger id="enhanceScaleFactor">
<SelectValue placeholder="自动缩放" />
</SelectTrigger>
<SelectContent>
<SelectItem value="auto">自动缩放</SelectItem>
<SelectItem value="1">不放大</SelectItem>
<SelectItem value="2">2倍放大</SelectItem>
<SelectItem value="4">4倍放大</SelectItem>
</SelectContent>
</Select>
<div className="text-xs text-muted-foreground">
选择放大倍数:自动(根据图片尺寸智能选择)、不放大、2倍或4倍
</div>
</div>
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4 space-y-2">
<div className="text-sm font-medium text-blue-900">API文档说明</div>
<div className="text-xs text-blue-800 space-y-1">
<p>• API地址应指向服务根地址,例如: https://techsz.aoscdn.com</p>
<p>• 系统会自动调用 /api/tasks/visual/scale 接口进行变清晰处理</p>
<p>• 支持的图片格式:JPG, JPEG, PNG, BMP, WEBP, TIFF等</p>
<p>• 图片大小:不超过20MB,分辨率最大4096x4096</p>
<p>• 返回的链接有效期为1小时,系统会自动下载并存储到S3</p>
</div>
</div>
{/* 主API配置 */}
{!enhanceConfig.useBackupApi && (
<>
<Separator />
<div className="text-sm font-medium text-muted-foreground">主API配置 (第三方云服务)</div>
<div className="space-y-2">
<Label htmlFor="enhanceApiUrl">API地址 *</Label>
<Input
id="enhanceApiUrl"
value={enhanceConfig.apiUrl}
onChange={(e) => setEnhanceConfig({ ...enhanceConfig, apiUrl: e.target.value })}
placeholder="https://techsz.aoscdn.com"
/>
<div className="text-xs text-muted-foreground">
请输入完整的API地址,例如: https://techsz.aoscdn.com
</div>
</div>
<div className="space-y-2">
<Label htmlFor="enhanceApiKey">API密钥 *</Label>
<Input
id="enhanceApiKey"
type="password"
value={enhanceConfig.apiKey}
onChange={(e) => setEnhanceConfig({ ...enhanceConfig, apiKey: e.target.value })}
placeholder="请输入API密钥"
/>
<div className="text-xs text-muted-foreground">
API密钥是必需的,请从服务提供商获取
</div>
</div>
<div className="space-y-2">
<Label htmlFor="enhanceType">修复类型</Label>
<Select
value={enhanceConfig.type}
onValueChange={(value) => setEnhanceConfig({ ...enhanceConfig, type: value })}
>
<SelectTrigger id="enhanceType">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="clean">通用放大清晰化</SelectItem>
<SelectItem value="face">人像放大清晰化</SelectItem>
</SelectContent>
</Select>
<div className="text-xs text-muted-foreground">
选择图片类型:通用或人像(人像模式最多同时处理10个人)
</div>
</div>
<div className="space-y-2">
<Label htmlFor="enhanceScaleFactor">放大倍数(可选)</Label>
<Select
value={enhanceConfig.scaleFactor}
onValueChange={(value) => setEnhanceConfig({ ...enhanceConfig, scaleFactor: value })}
>
<SelectTrigger id="enhanceScaleFactor">
<SelectValue placeholder="自动缩放" />
</SelectTrigger>
<SelectContent>
<SelectItem value="auto">自动缩放</SelectItem>
<SelectItem value="1">不放大</SelectItem>
<SelectItem value="2">2倍放大</SelectItem>
<SelectItem value="4">4倍放大</SelectItem>
</SelectContent>
</Select>
<div className="text-xs text-muted-foreground">
选择放大倍数:自动(根据图片尺寸智能选择)、不放大、2倍或4倍
</div>
</div>
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4 space-y-2">
<div className="text-sm font-medium text-blue-900">主API文档说明</div>
<div className="text-xs text-blue-800 space-y-1">
<p>• API地址应指向服务根地址,例如: https://techsz.aoscdn.com</p>
<p>• 系统会自动调用 /api/tasks/visual/scale 接口进行变清晰处理</p>
<p>• 支持的图片格式:JPG, JPEG, PNG, BMP, WEBP, TIFF等</p>
<p>• 图片大小:不超过20MB,分辨率最大4096x4096</p>
<p>• 返回的链接有效期为1小时,系统会自动下载并存储到S3</p>
</div>
</div>
</>
)}
{/* 备用API配置 (Real-ESRGAN) */}
{enhanceConfig.useBackupApi && (
<>
<Separator />
<div className="text-sm font-medium text-muted-foreground">备用API配置 (Real-ESRGAN 本地模型)</div>
<div className="space-y-2">
<Label htmlFor="backupApiUrl">API地址 *</Label>
<Input
id="backupApiUrl"
value={enhanceConfig.backupApiUrl}
onChange={(e) => setEnhanceConfig({ ...enhanceConfig, backupApiUrl: e.target.value })}
placeholder="http://156.225.27.52:5233"
/>
<div className="text-xs text-muted-foreground">
Real-ESRGAN API服务地址,默认: http://156.225.27.52:5233
</div>
</div>
<div className="space-y-2">
<Label htmlFor="backupApiKey">API密钥 *</Label>
<Input
id="backupApiKey"
type="password"
value={enhanceConfig.backupApiKey}
onChange={(e) => setEnhanceConfig({ ...enhanceConfig, backupApiKey: e.target.value })}
placeholder="请输入Real-ESRGAN API密钥"
/>
<div className="text-xs text-muted-foreground">
API密钥用于认证,请从管理员处获取
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="backupScale">放大倍数</Label>
<Select
value={enhanceConfig.backupScale}
onValueChange={(value) => setEnhanceConfig({ ...enhanceConfig, backupScale: value })}
>
<SelectTrigger id="backupScale">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="2">2倍放大</SelectItem>
<SelectItem value="4">4倍放大(推荐)</SelectItem>
<SelectItem value="8">8倍放大</SelectItem>
</SelectContent>
</Select>
<div className="text-xs text-muted-foreground">
推荐使用4倍放大,效果最佳
</div>
</div>
<div className="space-y-2">
<Label htmlFor="backupModel">模型选择</Label>
<Select
value={enhanceConfig.backupModel}
onValueChange={(value) => setEnhanceConfig({ ...enhanceConfig, backupModel: value })}
>
<SelectTrigger id="backupModel">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="RealESRGAN_x2plus">RealESRGAN x2plus (2倍)</SelectItem>
<SelectItem value="RealESRGAN_x4plus">RealESRGAN x4plus (推荐)</SelectItem>
<SelectItem value="RealESRGAN_x8plus">RealESRGAN x8plus (8倍)</SelectItem>
<SelectItem value="RealESRGAN_animevideov3">动漫视频模型 (4倍)</SelectItem>
</SelectContent>
</Select>
<div className="text-xs text-muted-foreground">
不同模型适用于不同场景
</div>
</div>
</div>
<div className="rounded-lg border border-green-200 bg-green-50 p-4 space-y-2">
<div className="text-sm font-medium text-green-900">Real-ESRGAN API说明</div>
<div className="text-xs text-green-800 space-y-1">
<p>• 基于Real-ESRGAN深度学习模型,图像超分辨率效果更好</p>
<p>• 使用URL方式上传图片,处理后自动保存到S3存储</p>
<p>• 支持2x、4x、8x放大倍数</p>
<p>• 处理时间:通常1-10秒,取决于图片大小</p>
<p>• 支持格式:JPG, PNG, JPEG等常见图片格式</p>
<p>• 文件限制:最大50MB</p>
</div>
</div>
</>
)}
<Button onClick={handleSaveEnhanceConfig}>
<Save className="mr-2 h-4 w-4" /> 保存配置
</Button>

View File

@ -5,6 +5,20 @@ import { uploadFile } from "@/lib/storage/service";
import { db } from "@/lib/db";
import { generatedImage } from "@/lib/db/schema";
// 变清晰API配置类型
type EnhanceApiConfig = {
apiUrl?: string;
apiKey?: string;
type?: string; // "clean" | "face"
scaleFactor?: number | string; // 1 | 2 | 4 | "auto"
// 备用API配置 (Real-ESRGAN)
useBackupApi?: boolean;
backupApiUrl?: string;
backupApiKey?: string;
backupScale?: string; // "2" | "4" | "8"
backupModel?: string;
};
/**
* 压缩图片生成缩略图(使用 sharp)
*/
@ -84,6 +98,150 @@ async function uploadBase64ToS3(base64Data: string, mimeType: string = "image/jp
// 设置API路由最大执行时间为3分钟(180秒)
export const maxDuration = 180;
/**
* 使用Real-ESRGAN备用API处理图片(通过URL方式)
* @param imageUrl 图片URL(需要公开可访问)
* @param config 备用API配置
* @returns 处理后的图片Base64和S3 URL
*/
async function processWithRealESRGAN(
imageUrl: string,
config: {
backupApiUrl: string;
backupApiKey: string;
backupScale: string;
backupModel: string;
}
): Promise<{ imageBase64: string; imageUrl: string; processingTime: number }> {
const startTime = Date.now();
const apiUrl = config.backupApiUrl.endsWith("/")
? config.backupApiUrl.slice(0, -1)
: config.backupApiUrl;
const upscaleUrl = `${apiUrl}/upscale/url`;
console.log(`[Enhance-RealESRGAN] 调用URL方式API: ${upscaleUrl}`);
console.log(`[Enhance-RealESRGAN] 图片URL: ${imageUrl}`);
console.log(`[Enhance-RealESRGAN] 放大倍数: ${config.backupScale}x, 模型: ${config.backupModel}`);
const response = await fetch(upscaleUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": config.backupApiKey,
},
body: JSON.stringify({
image_url: imageUrl,
scale: Number.parseInt(config.backupScale) || 4,
model_name: config.backupModel || "RealESRGAN_x4plus",
}),
});
if (!response.ok) {
const errorText = await response.text();
console.error(`[Enhance-RealESRGAN] API请求失败: ${response.status} ${response.statusText}`);
console.error(`[Enhance-RealESRGAN] 错误详情: ${errorText}`);
throw new Error(`Real-ESRGAN API请求失败: ${response.status} - ${errorText || response.statusText}`);
}
const result = await response.json();
console.log(`[Enhance-RealESRGAN] API响应:`, {
success: result.success,
hasImageUrl: !!result.image_url,
processingTime: result.processing_time,
totalTime: result.total_time,
});
if (!result.success || !result.image_url) {
throw new Error(`Real-ESRGAN处理失败: ${JSON.stringify(result)}`);
}
// Real-ESRGAN API直接返回S3 URL,我们需要下载图片并转换为Base64
console.log(`[Enhance-RealESRGAN] 下载处理后的图片: ${result.image_url}`);
const imageResponse = await fetch(result.image_url);
if (!imageResponse.ok) {
throw new Error(`无法下载Real-ESRGAN处理后的图片: ${imageResponse.status}`);
}
const imageBuffer = await imageResponse.arrayBuffer();
const imageBase64 = Buffer.from(imageBuffer).toString("base64");
const processingTime = (Date.now() - startTime) / 1000;
console.log(`[Enhance-RealESRGAN] ✅ 处理完成,总耗时: ${processingTime.toFixed(2)}秒`);
console.log(`[Enhance-RealESRGAN] ✅ 处理后图片大小: ${imageBuffer.byteLength} bytes`);
return {
imageBase64,
imageUrl: result.image_url,
processingTime,
};
}
/**
* 使用Real-ESRGAN备用API处理图片(通过Base64方式,用于没有公开URL的图片)
* @param imageBase64 图片Base64数据
* @param config 备用API配置
* @returns 处理后的图片Base64
*/
async function processWithRealESRGANBase64(
imageBase64: string,
config: {
backupApiUrl: string;
backupApiKey: string;
backupScale: string;
backupModel: string;
}
): Promise<{ imageBase64: string; processingTime: number }> {
const startTime = Date.now();
const apiUrl = config.backupApiUrl.endsWith("/")
? config.backupApiUrl.slice(0, -1)
: config.backupApiUrl;
const upscaleUrl = `${apiUrl}/upscale/base64`;
console.log(`[Enhance-RealESRGAN] 调用Base64方式API: ${upscaleUrl}`);
console.log(`[Enhance-RealESRGAN] 放大倍数: ${config.backupScale}x, 模型: ${config.backupModel}`);
console.log(`[Enhance-RealESRGAN] 输入图片Base64长度: ${imageBase64.length}`);
const response = await fetch(upscaleUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": config.backupApiKey,
},
body: JSON.stringify({
image_base64: imageBase64,
scale: Number.parseInt(config.backupScale) || 4,
model_name: config.backupModel || "RealESRGAN_x4plus",
}),
});
if (!response.ok) {
const errorText = await response.text();
console.error(`[Enhance-RealESRGAN] API请求失败: ${response.status} ${response.statusText}`);
console.error(`[Enhance-RealESRGAN] 错误详情: ${errorText}`);
throw new Error(`Real-ESRGAN API请求失败: ${response.status} - ${errorText || response.statusText}`);
}
const result = await response.json();
console.log(`[Enhance-RealESRGAN] API响应:`, {
success: result.success,
hasImageBase64: !!result.image_base64,
processingTime: result.processing_time,
});
if (!result.success || !result.image_base64) {
throw new Error(`Real-ESRGAN处理失败: ${JSON.stringify(result)}`);
}
const processingTime = (Date.now() - startTime) / 1000;
console.log(`[Enhance-RealESRGAN] ✅ 处理完成,总耗时: ${processingTime.toFixed(2)}秒`);
console.log(`[Enhance-RealESRGAN] ✅ 处理后图片Base64长度: ${result.image_base64.length}`);
return {
imageBase64: result.image_base64,
processingTime,
};
}
/**
* 轮询获取任务结果(根据官方示例)
*/
@ -184,18 +342,28 @@ export async function POST(request: NextRequest) {
);
}
const config = enhanceConfig.value as {
apiUrl?: string;
apiKey?: string;
type?: string; // "clean" | "face"
scaleFactor?: number | string; // 1 | 2 | 4 | "auto"
};
const config = enhanceConfig.value as EnhanceApiConfig;
if (!config.apiUrl || !config.apiKey) {
return NextResponse.json(
{ error: "变清晰API地址或密钥未配置" },
{ status: 400 }
);
// 检查是否使用备用API
const useBackupApi = config.useBackupApi === true;
console.log(`[Enhance API] 使用${useBackupApi ? "备用API (Real-ESRGAN)" : "主API"}`);
if (useBackupApi) {
// 验证备用API配置
if (!config.backupApiUrl || !config.backupApiKey) {
return NextResponse.json(
{ error: "备用变清晰API地址或密钥未配置" },
{ status: 400 }
);
}
} else {
// 验证主API配置
if (!config.apiUrl || !config.apiKey) {
return NextResponse.json(
{ error: "变清晰API地址或密钥未配置" },
{ status: 400 }
);
}
}
// 2. 获取请求体(Base64图片或FormData)
@ -250,11 +418,86 @@ export async function POST(request: NextRequest) {
);
}
// ========== 备用API处理路径 (Real-ESRGAN) ==========
if (useBackupApi) {
console.log("[Enhance API] 使用Real-ESRGAN备用API处理...");
try {
// 使用Base64方式调用Real-ESRGAN API
const result = await processWithRealESRGANBase64(imageBase64, {
backupApiUrl: config.backupApiUrl!,
backupApiKey: config.backupApiKey!,
backupScale: config.backupScale || "4",
backupModel: config.backupModel || "RealESRGAN_x4plus",
});
// 上传处理后的图片到S3
let thumbnailUrl: string;
let originalUrl: string;
try {
console.log(`[Enhance-RealESRGAN] 📤 上传处理后的图片到S3...`);
const uploadResult = await uploadBase64ToS3(result.imageBase64, "image/png");
thumbnailUrl = uploadResult.thumbnail;
originalUrl = uploadResult.original;
console.log(`[Enhance-RealESRGAN] ✅ 上传成功: ${originalUrl}`);
} catch (uploadError) {
console.error("[Enhance-RealESRGAN] ❌ 上传到S3失败:", uploadError);
return NextResponse.json(
{
error: "图片上传到存储桶失败",
detail: uploadError instanceof Error ? uploadError.message : "未知错误"
},
{ status: 500 }
);
}
// 保存到数据库
try {
const [savedImage] = await db.insert(generatedImage).values({
userId,
chatId: chatId || null,
messageId: messageId || null,
thumbnailUrl,
originalUrl,
imageSize: `${config.backupScale}x放大`,
prompt: prompt || "Real-ESRGAN图片变清晰处理",
modelId: `realesrgan-${config.backupModel}`,
}).returning();
console.log(`[Enhance-RealESRGAN] ✅ 保存到数据库成功,ID: ${savedImage.id}`);
} catch (dbError) {
console.error("[Enhance-RealESRGAN] ⚠️ 保存到数据库失败:", dbError);
// 继续返回成功,图片已处理并上传
}
// 返回结果
return NextResponse.json({
success: true,
image_base64: result.imageBase64,
thumbnail_url: thumbnailUrl,
original_url: originalUrl,
processing_time: result.processingTime,
api_type: "realesrgan",
});
} catch (error) {
console.error("[Enhance-RealESRGAN] ❌ 处理失败:", error);
return NextResponse.json(
{
error: "Real-ESRGAN变清晰处理失败",
detail: error instanceof Error ? error.message : "未知错误",
},
{ status: 500 }
);
}
}
// ========== 主API处理路径 ==========
// 3. 调用变清晰API(使用异步模式,根据官方示例)
console.log("[Enhance API] 开始准备API调用...");
const apiUrl = config.apiUrl.endsWith("/")
? config.apiUrl.slice(0, -1)
: config.apiUrl;
console.log("[Enhance API] 开始准备主API调用...");
const apiUrl = config.apiUrl!.endsWith("/")
? config.apiUrl!.slice(0, -1)
: config.apiUrl!;
const enhanceUrl = `${apiUrl}/api/tasks/visual/scale`;
console.log("[Enhance API] API URL:", enhanceUrl);
@ -283,7 +526,7 @@ export async function POST(request: NextRequest) {
formData.append("return_type", "2"); // 返回base64
const headers: Record<string, string> = {
"X-API-KEY": config.apiKey,
"X-API-KEY": config.apiKey!,
...formData.getHeaders(), // 包含Content-Type和boundary
};
@ -418,7 +661,7 @@ export async function POST(request: NextRequest) {
// 4. 轮询获取任务结果(根据官方示例)
console.log(`[Enhance] 开始轮询任务结果,task_id: ${taskId}`);
const taskResult = await pollTaskResult(apiUrl, config.apiKey, taskId);
const taskResult = await pollTaskResult(apiUrl, config.apiKey!, taskId);
const finalProcessingTime = (Date.now() - startTime) / 1000;

View File

@ -84,3 +84,5 @@ echo ""

View File

@ -137,3 +137,5 @@ pmset -g