- 新增用户数据导出功能: * 支持导出当前查询条件下的数据 * 支持导出全部用户数据 * 支持下载用户导入模板 * 自动生成带时间戳的文件名 - 新增用户数据导入功能: * 拖拽上传Excel文件支持 * 文件类型和大小验证(.xlsx/.xls, 最大10MB) * 覆盖已存在数据选项 * 实时上传进度显示 * 详细的导入结果反馈 - 完善API接口: * downloadExcelTemplate: 下载Excel模板 * exportExcelData: 导出用户数据(支持查询参数) * importUserData: 导入用户数据(支持覆盖选项) - UI/UX优化: * 下拉菜单式导出按钮设计 * 专业的导入模态框界面 * 完善的错误处理和用户反馈 * 导入说明和操作指导 技术实现: - 使用Blob处理文件下载 - FormData处理文件上传 - Naive UI组件深度集成 - TypeScript类型安全保障
871 lines
24 KiB
Markdown
871 lines
24 KiB
Markdown
# 图片管理API
|
||
|
||
## 概述
|
||
|
||
图片管理模块专门用于管理系统中的图片资源,提供图片的上传、存储、展示和管理功能。支持多种图片格式,具备图片压缩、水印、缩略图生成等高级功能。
|
||
|
||
## 权限说明
|
||
|
||
图片管理接口需要相应的权限才能访问:
|
||
|
||
| 操作 | 权限码 | 说明 |
|
||
|------|--------|------|
|
||
| 查询图片列表 | `system:picture:list` | 查看图片列表权限 |
|
||
| 上传图片 | `system:picture:upload` | 上传图片权限 |
|
||
| 删除图片 | `system:picture:remove` | 删除图片权限 |
|
||
| 编辑图片 | `system:picture:edit` | 编辑图片信息权限 |
|
||
|
||
## 接口列表
|
||
|
||
### 1. 分页查询图片列表
|
||
|
||
**接口地址**: `GET /coder/sysPicture/listPage`
|
||
|
||
**接口描述**: 分页查询系统图片列表
|
||
|
||
**是否需要认证**: 是
|
||
|
||
**权限要求**: `system:picture:list`
|
||
|
||
**请求头**:
|
||
```
|
||
Authorization: Bearer your-token-value
|
||
```
|
||
|
||
**请求参数**:
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|
||
|--------|------|------|------|------|
|
||
| pageNo | Integer | 否 | 页码 | 1 |
|
||
| pageSize | Integer | 否 | 每页大小 | 10 |
|
||
| pictureName | String | 否 | 图片名称 | banner.jpg |
|
||
| pictureType | String | 否 | 图片类型 | jpg |
|
||
| albumName | String | 否 | 相册名称 | banner |
|
||
| pictureStatus | String | 否 | 图片状态 | 0 |
|
||
| beginTime | String | 否 | 开始时间 | 2024-01-01 |
|
||
| endTime | String | 否 | 结束时间 | 2024-12-31 |
|
||
|
||
**响应示例**:
|
||
|
||
```json
|
||
{
|
||
"status": 200,
|
||
"msg": "SUCCESS",
|
||
"data": {
|
||
"records": [
|
||
{
|
||
"pictureId": 1,
|
||
"pictureName": "banner_20240705_001.jpg",
|
||
"originalName": "主页横幅.jpg",
|
||
"picturePath": "/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||
"pictureUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||
"thumbnailUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/thumb_banner_20240705_001.jpg",
|
||
"albumName": "banner",
|
||
"pictureSize": 2048000,
|
||
"pictureType": "jpg",
|
||
"pictureFormat": "JPEG",
|
||
"width": 1920,
|
||
"height": 1080,
|
||
"pictureStatus": "0",
|
||
"isWatermark": "0",
|
||
"uploadUserId": 1,
|
||
"uploadUserName": "admin",
|
||
"description": "主页横幅图片",
|
||
"tags": "横幅,主页,展示",
|
||
"viewCount": 100,
|
||
"downloadCount": 10,
|
||
"remark": "网站主页横幅",
|
||
"createBy": "admin",
|
||
"createTime": "2024-07-05 10:00:00",
|
||
"updateBy": "admin",
|
||
"updateTime": "2024-07-05 10:00:00"
|
||
}
|
||
],
|
||
"total": 1,
|
||
"size": 10,
|
||
"current": 1,
|
||
"pages": 1
|
||
},
|
||
"traceId": "trace-123456"
|
||
}
|
||
```
|
||
|
||
**调用示例**:
|
||
|
||
```bash
|
||
curl -X GET \
|
||
"http://localhost:18099/coder/sysPicture/listPage?pageNo=1&pageSize=10&albumName=banner" \
|
||
-H "Authorization: Bearer your-token-value"
|
||
```
|
||
|
||
---
|
||
|
||
### 2. 查询所有图片
|
||
|
||
**接口地址**: `GET /coder/sysPicture/list`
|
||
|
||
**接口描述**: 查询所有系统图片(不分页)
|
||
|
||
**是否需要认证**: 是
|
||
|
||
**权限要求**: `system:picture:list`
|
||
|
||
**请求参数**: 同分页查询(除pageNo、pageSize外)
|
||
|
||
**响应示例**:
|
||
|
||
```json
|
||
{
|
||
"status": 200,
|
||
"msg": "SUCCESS",
|
||
"data": [
|
||
{
|
||
"pictureId": 1,
|
||
"pictureName": "banner_20240705_001.jpg",
|
||
"originalName": "主页横幅.jpg",
|
||
"picturePath": "/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||
"pictureUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||
"thumbnailUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/thumb_banner_20240705_001.jpg",
|
||
"albumName": "banner",
|
||
"pictureSize": 2048000,
|
||
"pictureType": "jpg",
|
||
"pictureFormat": "JPEG",
|
||
"width": 1920,
|
||
"height": 1080,
|
||
"pictureStatus": "0",
|
||
"isWatermark": "0",
|
||
"uploadUserId": 1,
|
||
"uploadUserName": "admin",
|
||
"description": "主页横幅图片",
|
||
"tags": "横幅,主页,展示",
|
||
"viewCount": 100,
|
||
"downloadCount": 10,
|
||
"remark": "网站主页横幅",
|
||
"createBy": "admin",
|
||
"createTime": "2024-07-05 10:00:00",
|
||
"updateBy": "admin",
|
||
"updateTime": "2024-07-05 10:00:00"
|
||
}
|
||
],
|
||
"traceId": "trace-123456"
|
||
}
|
||
```
|
||
|
||
**调用示例**:
|
||
|
||
```bash
|
||
curl -X GET \
|
||
"http://localhost:18099/coder/sysPicture/list?albumName=banner" \
|
||
-H "Authorization: Bearer your-token-value"
|
||
```
|
||
|
||
---
|
||
|
||
### 3. 根据ID查询图片
|
||
|
||
**接口地址**: `GET /coder/sysPicture/getById/{id}`
|
||
|
||
**接口描述**: 根据图片ID查询图片详细信息
|
||
|
||
**是否需要认证**: 是
|
||
|
||
**权限要求**: `system:picture:list`
|
||
|
||
**路径参数**:
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 |
|
||
|--------|------|------|------|
|
||
| id | Long | 是 | 图片ID |
|
||
|
||
**响应示例**:
|
||
|
||
```json
|
||
{
|
||
"status": 200,
|
||
"msg": "SUCCESS",
|
||
"data": {
|
||
"pictureId": 1,
|
||
"pictureName": "banner_20240705_001.jpg",
|
||
"originalName": "主页横幅.jpg",
|
||
"picturePath": "/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||
"pictureUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg",
|
||
"thumbnailUrl": "http://localhost:18099/upload/pictures/banner/2024/07/05/thumb_banner_20240705_001.jpg",
|
||
"albumName": "banner",
|
||
"pictureSize": 2048000,
|
||
"pictureType": "jpg",
|
||
"pictureFormat": "JPEG",
|
||
"width": 1920,
|
||
"height": 1080,
|
||
"pictureStatus": "0",
|
||
"isWatermark": "0",
|
||
"uploadUserId": 1,
|
||
"uploadUserName": "admin",
|
||
"description": "主页横幅图片",
|
||
"tags": "横幅,主页,展示",
|
||
"viewCount": 100,
|
||
"downloadCount": 10,
|
||
"remark": "网站主页横幅",
|
||
"createBy": "admin",
|
||
"createTime": "2024-07-05 10:00:00",
|
||
"updateBy": "admin",
|
||
"updateTime": "2024-07-05 10:00:00"
|
||
},
|
||
"traceId": "trace-123456"
|
||
}
|
||
```
|
||
|
||
**调用示例**:
|
||
|
||
```bash
|
||
curl -X GET \
|
||
http://localhost:18099/coder/sysPicture/getById/1 \
|
||
-H "Authorization: Bearer your-token-value"
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 新增图片记录
|
||
|
||
**接口地址**: `POST /coder/sysPicture/add`
|
||
|
||
**接口描述**: 新增系统图片记录
|
||
|
||
**是否需要认证**: 是
|
||
|
||
**权限要求**: `system:picture:upload`
|
||
|
||
**请求参数**:
|
||
|
||
```json
|
||
{
|
||
"pictureName": "product_20240705_001.jpg",
|
||
"originalName": "产品展示图.jpg",
|
||
"picturePath": "/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||
"pictureUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||
"thumbnailUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/thumb_product_20240705_001.jpg",
|
||
"albumName": "product",
|
||
"pictureSize": 1536000,
|
||
"pictureType": "jpg",
|
||
"pictureFormat": "JPEG",
|
||
"width": 1200,
|
||
"height": 800,
|
||
"pictureStatus": "0",
|
||
"isWatermark": "1",
|
||
"uploadUserId": 1,
|
||
"uploadUserName": "admin",
|
||
"description": "产品展示图片",
|
||
"tags": "产品,展示,商品",
|
||
"remark": "商品详情页图片"
|
||
}
|
||
```
|
||
|
||
**请求参数说明**:
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|--------|------|------|------|----------|
|
||
| pictureName | String | 是 | 存储图片名 | 不能为空 |
|
||
| originalName | String | 是 | 原始图片名 | 不能为空 |
|
||
| picturePath | String | 是 | 图片相对路径 | 不能为空 |
|
||
| pictureUrl | String | 是 | 图片访问URL | 不能为空 |
|
||
| thumbnailUrl | String | 否 | 缩略图URL | 可为空 |
|
||
| albumName | String | 是 | 相册名称 | 不能为空 |
|
||
| pictureSize | Long | 是 | 图片大小 | 必须大于0 |
|
||
| pictureType | String | 是 | 图片类型 | 不能为空 |
|
||
| pictureFormat | String | 是 | 图片格式 | 不能为空 |
|
||
| width | Integer | 否 | 图片宽度 | 大于0 |
|
||
| height | Integer | 否 | 图片高度 | 大于0 |
|
||
| pictureStatus | String | 是 | 图片状态 | 0-正常 1-删除 |
|
||
| isWatermark | String | 否 | 是否水印 | 0-否 1-是 |
|
||
| uploadUserId | Long | 否 | 上传用户ID | 有效的用户ID |
|
||
| uploadUserName | String | 否 | 上传用户名 | 可为空 |
|
||
| description | String | 否 | 图片描述 | 最长500字符 |
|
||
| tags | String | 否 | 图片标签 | 逗号分隔 |
|
||
| remark | String | 否 | 备注信息 | 最长200字符 |
|
||
|
||
**响应示例**:
|
||
|
||
```json
|
||
{
|
||
"status": 200,
|
||
"msg": "SUCCESS",
|
||
"data": "新增成功",
|
||
"traceId": "trace-123456"
|
||
}
|
||
```
|
||
|
||
**调用示例**:
|
||
|
||
```bash
|
||
curl -X POST \
|
||
http://localhost:18099/coder/sysPicture/add \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer your-token-value" \
|
||
-d '{
|
||
"pictureName": "product_20240705_001.jpg",
|
||
"originalName": "产品展示图.jpg",
|
||
"picturePath": "/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||
"pictureUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||
"albumName": "product",
|
||
"pictureSize": 1536000,
|
||
"pictureType": "jpg",
|
||
"pictureFormat": "JPEG",
|
||
"width": 1200,
|
||
"height": 800,
|
||
"pictureStatus": "0",
|
||
"description": "产品展示图片",
|
||
"tags": "产品,展示,商品"
|
||
}'
|
||
```
|
||
|
||
---
|
||
|
||
### 5. 修改图片信息
|
||
|
||
**接口地址**: `POST /coder/sysPicture/update`
|
||
|
||
**接口描述**: 修改系统图片记录信息
|
||
|
||
**是否需要认证**: 是
|
||
|
||
**权限要求**: `system:picture:edit`
|
||
|
||
**请求参数**:
|
||
|
||
```json
|
||
{
|
||
"pictureId": 1,
|
||
"pictureName": "product_20240705_001.jpg",
|
||
"originalName": "产品展示图.jpg",
|
||
"picturePath": "/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||
"pictureUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/product_20240705_001.jpg",
|
||
"thumbnailUrl": "http://localhost:18099/upload/pictures/product/2024/07/05/thumb_product_20240705_001.jpg",
|
||
"albumName": "product",
|
||
"pictureSize": 1536000,
|
||
"pictureType": "jpg",
|
||
"pictureFormat": "JPEG",
|
||
"width": 1200,
|
||
"height": 800,
|
||
"pictureStatus": "0",
|
||
"isWatermark": "1",
|
||
"uploadUserId": 1,
|
||
"uploadUserName": "admin",
|
||
"description": "产品展示图片(已更新)",
|
||
"tags": "产品,展示,商品,新品",
|
||
"remark": "商品详情页图片(已更新)"
|
||
}
|
||
```
|
||
|
||
**请求参数说明**:
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|--------|------|------|------|----------|
|
||
| pictureId | Long | 是 | 图片ID | 必须是有效的图片ID |
|
||
| 其他参数 | - | - | 同新增图片记录 | - |
|
||
|
||
**响应示例**:
|
||
|
||
```json
|
||
{
|
||
"status": 200,
|
||
"msg": "SUCCESS",
|
||
"data": "修改成功",
|
||
"traceId": "trace-123456"
|
||
}
|
||
```
|
||
|
||
**调用示例**:
|
||
|
||
```bash
|
||
curl -X POST \
|
||
http://localhost:18099/coder/sysPicture/update \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer your-token-value" \
|
||
-d '{
|
||
"pictureId": 1,
|
||
"description": "产品展示图片(已更新)",
|
||
"tags": "产品,展示,商品,新品"
|
||
}'
|
||
```
|
||
|
||
---
|
||
|
||
### 6. 删除图片
|
||
|
||
**接口地址**: `POST /coder/sysPicture/deleteById/{id}`
|
||
|
||
**接口描述**: 根据ID删除图片记录和物理文件
|
||
|
||
**是否需要认证**: 是
|
||
|
||
**权限要求**: `system:picture:remove`
|
||
|
||
**路径参数**:
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 |
|
||
|--------|------|------|------|
|
||
| id | Long | 是 | 图片ID |
|
||
|
||
**响应示例**:
|
||
|
||
```json
|
||
{
|
||
"status": 200,
|
||
"msg": "SUCCESS",
|
||
"data": "删除成功",
|
||
"traceId": "trace-123456"
|
||
}
|
||
```
|
||
|
||
**调用示例**:
|
||
|
||
```bash
|
||
curl -X POST \
|
||
http://localhost:18099/coder/sysPicture/deleteById/1 \
|
||
-H "Authorization: Bearer your-token-value"
|
||
```
|
||
|
||
---
|
||
|
||
### 7. 批量删除图片
|
||
|
||
**接口地址**: `POST /coder/sysPicture/batchDelete`
|
||
|
||
**接口描述**: 批量删除图片记录和物理文件
|
||
|
||
**是否需要认证**: 是
|
||
|
||
**权限要求**: `system:picture:remove`
|
||
|
||
**请求参数**:
|
||
|
||
```json
|
||
{
|
||
"ids": [1, 2, 3]
|
||
}
|
||
```
|
||
|
||
**请求参数说明**:
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 |
|
||
|--------|------|------|------|
|
||
| ids | Long[] | 是 | 图片ID数组 |
|
||
|
||
**响应示例**:
|
||
|
||
```json
|
||
{
|
||
"status": 200,
|
||
"msg": "SUCCESS",
|
||
"data": "删除成功",
|
||
"traceId": "trace-123456"
|
||
}
|
||
```
|
||
|
||
**调用示例**:
|
||
|
||
```bash
|
||
curl -X POST \
|
||
http://localhost:18099/coder/sysPicture/batchDelete \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer your-token-value" \
|
||
-d '{
|
||
"ids": [1, 2, 3]
|
||
}'
|
||
```
|
||
|
||
---
|
||
|
||
## 图片字段说明
|
||
|
||
### 基础字段
|
||
|
||
| 字段名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| pictureId | Long | 图片ID | 1 |
|
||
| pictureName | String | 存储图片名 | banner_20240705_001.jpg |
|
||
| originalName | String | 原始图片名 | 主页横幅.jpg |
|
||
| picturePath | String | 图片相对路径 | /upload/pictures/banner/2024/07/05/banner_20240705_001.jpg |
|
||
| pictureUrl | String | 图片访问URL | http://localhost:18099/upload/pictures/banner/2024/07/05/banner_20240705_001.jpg |
|
||
| thumbnailUrl | String | 缩略图URL | http://localhost:18099/upload/pictures/banner/2024/07/05/thumb_banner_20240705_001.jpg |
|
||
|
||
### 分类字段
|
||
|
||
| 字段名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| albumName | String | 相册名称 | banner |
|
||
| pictureType | String | 图片扩展名 | jpg |
|
||
| pictureFormat | String | 图片格式 | JPEG |
|
||
| pictureSize | Long | 图片大小(字节) | 2048000 |
|
||
|
||
### 尺寸字段
|
||
|
||
| 字段名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| width | Integer | 图片宽度(像素) | 1920 |
|
||
| height | Integer | 图片高度(像素) | 1080 |
|
||
|
||
### 状态字段
|
||
|
||
| 字段名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| pictureStatus | String | 图片状态 | 0-正常 1-删除 |
|
||
| isWatermark | String | 是否有水印 | 0-否 1-是 |
|
||
| uploadUserId | Long | 上传用户ID | 1 |
|
||
| uploadUserName | String | 上传用户名 | admin |
|
||
|
||
### 描述字段
|
||
|
||
| 字段名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| description | String | 图片描述 | 主页横幅图片 |
|
||
| tags | String | 图片标签 | 横幅,主页,展示 |
|
||
| remark | String | 备注信息 | 网站主页横幅 |
|
||
|
||
### 统计字段
|
||
|
||
| 字段名 | 类型 | 说明 | 示例 |
|
||
|--------|------|------|------|
|
||
| viewCount | Integer | 查看次数 | 100 |
|
||
| downloadCount | Integer | 下载次数 | 10 |
|
||
|
||
---
|
||
|
||
## 数据字典
|
||
|
||
### 图片状态 (pictureStatus)
|
||
|
||
| 值 | 说明 |
|
||
|----|------|
|
||
| 0 | 正常 |
|
||
| 1 | 已删除 |
|
||
|
||
### 是否水印 (isWatermark)
|
||
|
||
| 值 | 说明 |
|
||
|----|------|
|
||
| 0 | 无水印 |
|
||
| 1 | 有水印 |
|
||
|
||
### 支持的图片格式
|
||
|
||
| 格式 | 扩展名 | MIME类型 | 说明 |
|
||
|------|--------|----------|------|
|
||
| JPEG | jpg, jpeg | image/jpeg | 有损压缩,适合照片 |
|
||
| PNG | png | image/png | 无损压缩,支持透明 |
|
||
| GIF | gif | image/gif | 支持动画 |
|
||
| WebP | webp | image/webp | 现代格式,压缩率高 |
|
||
| BMP | bmp | image/bmp | 位图格式 |
|
||
| TIFF | tiff, tif | image/tiff | 高质量图像 |
|
||
|
||
### 相册分类
|
||
|
||
| 相册名称 | 说明 | 用途 |
|
||
|----------|------|------|
|
||
| banner | 横幅图片 | 网站横幅展示 |
|
||
| product | 产品图片 | 商品展示 |
|
||
| avatar | 头像图片 | 用户头像 |
|
||
| gallery | 图库图片 | 相册展示 |
|
||
| article | 文章图片 | 文章配图 |
|
||
| icon | 图标图片 | 小图标 |
|
||
| background | 背景图片 | 页面背景 |
|
||
|
||
---
|
||
|
||
## 图片处理功能
|
||
|
||
### 1. 图片上传处理
|
||
|
||
```java
|
||
@Service
|
||
public class PictureUploadService {
|
||
|
||
/**
|
||
* 上传图片
|
||
*/
|
||
public PictureUploadResult uploadPicture(MultipartFile file, String album) {
|
||
// 1. 图片校验
|
||
validatePicture(file);
|
||
|
||
// 2. 生成文件名
|
||
String fileName = generatePictureName(file.getOriginalFilename());
|
||
|
||
// 3. 保存原图
|
||
String originalPath = savePicture(file, album, fileName);
|
||
|
||
// 4. 生成缩略图
|
||
String thumbnailPath = generateThumbnail(originalPath, album, fileName);
|
||
|
||
// 5. 添加水印
|
||
if (needWatermark(album)) {
|
||
addWatermark(originalPath);
|
||
}
|
||
|
||
// 6. 获取图片信息
|
||
BufferedImage image = ImageIO.read(new File(originalPath));
|
||
int width = image.getWidth();
|
||
int height = image.getHeight();
|
||
|
||
// 7. 生成访问URL
|
||
String pictureUrl = generatePictureUrl(album, fileName);
|
||
String thumbnailUrl = generateThumbnailUrl(album, fileName);
|
||
|
||
// 8. 返回结果
|
||
return PictureUploadResult.builder()
|
||
.pictureName(fileName)
|
||
.originalName(file.getOriginalFilename())
|
||
.picturePath(originalPath)
|
||
.pictureUrl(pictureUrl)
|
||
.thumbnailUrl(thumbnailUrl)
|
||
.pictureSize(file.getSize())
|
||
.pictureType(getFileExtension(fileName))
|
||
.pictureFormat(getImageFormat(file))
|
||
.width(width)
|
||
.height(height)
|
||
.build();
|
||
}
|
||
|
||
/**
|
||
* 生成缩略图
|
||
*/
|
||
private String generateThumbnail(String originalPath, String album, String fileName) {
|
||
try {
|
||
BufferedImage originalImage = ImageIO.read(new File(originalPath));
|
||
|
||
// 计算缩略图尺寸
|
||
int thumbnailWidth = 200;
|
||
int thumbnailHeight = 150;
|
||
|
||
// 保持宽高比
|
||
double ratio = Math.min((double) thumbnailWidth / originalImage.getWidth(),
|
||
(double) thumbnailHeight / originalImage.getHeight());
|
||
int width = (int) (originalImage.getWidth() * ratio);
|
||
int height = (int) (originalImage.getHeight() * ratio);
|
||
|
||
// 生成缩略图
|
||
BufferedImage thumbnailImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||
Graphics2D g2d = thumbnailImage.createGraphics();
|
||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||
g2d.drawImage(originalImage, 0, 0, width, height, null);
|
||
g2d.dispose();
|
||
|
||
// 保存缩略图
|
||
String thumbnailPath = getThumbnailPath(album, fileName);
|
||
ImageIO.write(thumbnailImage, "jpg", new File(thumbnailPath));
|
||
|
||
return thumbnailPath;
|
||
} catch (IOException e) {
|
||
throw new BusinessException("生成缩略图失败", e);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 添加水印
|
||
*/
|
||
private void addWatermark(String imagePath) {
|
||
try {
|
||
BufferedImage originalImage = ImageIO.read(new File(imagePath));
|
||
Graphics2D g2d = originalImage.createGraphics();
|
||
|
||
// 设置水印属性
|
||
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
|
||
g2d.setColor(Color.LIGHT_GRAY);
|
||
g2d.setFont(new Font("Arial", Font.BOLD, 20));
|
||
|
||
// 计算水印位置
|
||
FontMetrics fm = g2d.getFontMetrics();
|
||
String watermarkText = "© Your Company";
|
||
int x = originalImage.getWidth() - fm.stringWidth(watermarkText) - 10;
|
||
int y = originalImage.getHeight() - fm.getHeight() + fm.getAscent() - 10;
|
||
|
||
// 绘制水印
|
||
g2d.drawString(watermarkText, x, y);
|
||
g2d.dispose();
|
||
|
||
// 保存带水印的图片
|
||
ImageIO.write(originalImage, "jpg", new File(imagePath));
|
||
} catch (IOException e) {
|
||
throw new BusinessException("添加水印失败", e);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2. 图片压缩
|
||
|
||
```java
|
||
/**
|
||
* 图片压缩
|
||
*/
|
||
public void compressPicture(String inputPath, String outputPath, float quality) {
|
||
try {
|
||
BufferedImage image = ImageIO.read(new File(inputPath));
|
||
|
||
// 创建输出流
|
||
FileOutputStream fos = new FileOutputStream(outputPath);
|
||
|
||
// 获取JPEG写入器
|
||
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
|
||
ImageWriter writer = writers.next();
|
||
|
||
// 设置输出
|
||
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
|
||
writer.setOutput(ios);
|
||
|
||
// 设置压缩参数
|
||
ImageWriteParam param = writer.getDefaultWriteParam();
|
||
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||
param.setCompressionQuality(quality); // 压缩质量 0.0-1.0
|
||
|
||
// 写入图片
|
||
writer.write(null, new IIOImage(image, null, null), param);
|
||
|
||
// 清理资源
|
||
ios.close();
|
||
fos.close();
|
||
writer.dispose();
|
||
} catch (IOException e) {
|
||
throw new BusinessException("图片压缩失败", e);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 图片格式转换
|
||
|
||
```java
|
||
/**
|
||
* 图片格式转换
|
||
*/
|
||
public void convertPictureFormat(String inputPath, String outputPath, String format) {
|
||
try {
|
||
BufferedImage image = ImageIO.read(new File(inputPath));
|
||
|
||
// 如果转换为JPEG,需要处理透明背景
|
||
if ("jpg".equalsIgnoreCase(format) || "jpeg".equalsIgnoreCase(format)) {
|
||
BufferedImage jpegImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
|
||
Graphics2D g2d = jpegImage.createGraphics();
|
||
g2d.setColor(Color.WHITE);
|
||
g2d.fillRect(0, 0, image.getWidth(), image.getHeight());
|
||
g2d.drawImage(image, 0, 0, null);
|
||
g2d.dispose();
|
||
image = jpegImage;
|
||
}
|
||
|
||
// 保存转换后的图片
|
||
ImageIO.write(image, format, new File(outputPath));
|
||
} catch (IOException e) {
|
||
throw new BusinessException("图片格式转换失败", e);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 错误码说明
|
||
|
||
| 错误码 | 错误信息 | 说明 |
|
||
|--------|----------|------|
|
||
| 400 | 图片不能为空 | 上传图片为空 |
|
||
| 400 | 图片大小不能超过{size}MB | 图片大小超过限制 |
|
||
| 400 | 不支持的图片格式 | 图片格式不支持 |
|
||
| 400 | 图片尺寸超过限制 | 图片宽高超过限制 |
|
||
| 400 | 图片名称不能为空 | 图片名称为空 |
|
||
| 400 | 相册名称不能为空 | 相册名称为空 |
|
||
| 400 | 图片不存在 | 图片ID不存在 |
|
||
| 400 | 图片已被删除 | 图片状态为已删除 |
|
||
| 401 | 当前会话未登录 | 未登录或Token无效 |
|
||
| 403 | 权限不足 | 没有相应的操作权限 |
|
||
| 500 | 图片上传失败 | 图片保存到磁盘失败 |
|
||
| 500 | 缩略图生成失败 | 缩略图生成过程失败 |
|
||
| 500 | 水印添加失败 | 水印添加过程失败 |
|
||
| 500 | 图片压缩失败 | 图片压缩过程失败 |
|
||
| 500 | 图片删除失败 | 物理文件删除失败 |
|
||
|
||
---
|
||
|
||
## 安全特性
|
||
|
||
### 1. 图片验证
|
||
|
||
- **格式验证**: 验证图片格式和MIME类型
|
||
- **尺寸验证**: 验证图片宽高限制
|
||
- **内容验证**: 验证图片内容安全性
|
||
- **病毒扫描**: 对上传图片进行病毒扫描
|
||
|
||
### 2. 访问控制
|
||
|
||
- **权限验证**: 验证用户访问权限
|
||
- **防盗链**: 防止图片被盗链
|
||
- **水印保护**: 添加水印保护版权
|
||
- **下载限制**: 限制图片下载次数
|
||
|
||
### 3. 存储安全
|
||
|
||
- **路径安全**: 防止路径遍历攻击
|
||
- **文件重命名**: 重命名避免冲突
|
||
- **备份策略**: 重要图片定期备份
|
||
- **权限设置**: 设置合适的文件权限
|
||
|
||
---
|
||
|
||
## 性能优化
|
||
|
||
### 1. 图片处理优化
|
||
|
||
- **异步处理**: 图片压缩和水印添加异步处理
|
||
- **批量处理**: 支持批量图片处理
|
||
- **缓存策略**: 缓存处理后的图片
|
||
- **CDN加速**: 使用CDN加速图片访问
|
||
|
||
### 2. 存储优化
|
||
|
||
- **分目录存储**: 按相册和日期分目录存储
|
||
- **格式优化**: 自动选择最优图片格式
|
||
- **压缩存储**: 自动压缩降低存储空间
|
||
- **重复检测**: 检测重复图片避免冗余
|
||
|
||
### 3. 访问优化
|
||
|
||
- **懒加载**: 图片懒加载技术
|
||
- **响应式图片**: 根据设备提供不同尺寸
|
||
- **WebP支持**: 支持WebP格式提高性能
|
||
- **预加载**: 关键图片预加载
|
||
|
||
---
|
||
|
||
## 使用建议
|
||
|
||
### 1. 图片命名规范
|
||
|
||
- **唯一性**: 确保图片名唯一
|
||
- **时间戳**: 包含时间戳信息
|
||
- **分类标识**: 包含相册分类信息
|
||
- **版本控制**: 支持图片版本管理
|
||
|
||
### 2. 相册管理
|
||
|
||
- **分类管理**: 按用途分类管理图片
|
||
- **权限控制**: 不同相册设置不同权限
|
||
- **容量限制**: 设置相册容量限制
|
||
- **定期清理**: 定期清理无用图片
|
||
|
||
### 3. 图片优化
|
||
|
||
- **尺寸适配**: 根据用途选择合适尺寸
|
||
- **格式选择**: 根据内容选择最优格式
|
||
- **质量平衡**: 平衡图片质量和文件大小
|
||
- **缓存策略**: 设置合适的缓存时间
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
1. **图片安全**: 严格验证上传图片格式和内容
|
||
2. **版权保护**: 添加水印保护图片版权
|
||
3. **存储管理**: 定期清理无效和重复图片
|
||
4. **性能考虑**: 大图片上传时注意性能影响
|
||
5. **备份策略**: 重要图片需要备份
|
||
6. **访问控制**: 合理设置图片访问权限
|
||
7. **格式支持**: 根据需要支持不同图片格式
|
||
8. **缩略图**: 为提高加载速度生成缩略图
|
||
9. **压缩处理**: 适当压缩减少存储空间
|
||
10. **监控告警**: 监控存储空间和访问异常 |