# 图片管理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 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. **监控告警**: 监控存储空间和访问异常