chenhj
7 天以前 8c58f304f8345e046b7d71481dae7ad4eebee27e
增加永久文件接口
已添加1个文件
已修改1个文件
738 ■■■■■ 文件已修改
FILE_UPLOAD_README.md 734 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev-pro.yml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
FILE_UPLOAD_README.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,734 @@
# æ–‡ä»¶ä¸Šä¼ åŠŸèƒ½è¯´æ˜Ž
本文档基于以下代码整理:
- `src/main/java/com/ruoyi/basic/utils/FileUtil.java`
- `src/main/java/com/ruoyi/basic/enums/ApplicationTypeEnum.java`
- `src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java`
- `src/main/java/com/ruoyi/project/common/CommonController.java`
- `src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java`
用于说明本项目中文件上传、附件绑定、文件预览/下载的整体设计,以及 `FileUtil` ä¸­æ¯ä¸ªæ–¹æ³•的作用。
## 1. æ•´ä½“设计
本项目的文件体系分成两层:
- `storage_blob`:存文件实体信息
  - åŽŸå§‹æ–‡ä»¶å
  - å”¯ä¸€æ–‡ä»¶å `uidFilename`
  - æ–‡ä»¶è·¯å¾„ `path`
  - æ–‡ä»¶å¤§å° `byteSize`
  - æ–‡ä»¶ç±»åž‹ `contentType`
  - å…¬å…±è®¿é—®æ ‡è¯† `resourceKey`
- `storage_attachment`:存文件和业务记录的关联关系
  - `application`:文件用途
  - `recordType`:业务记录类型
  - `recordId`:业务记录主键
  - `storageBlobId`:关联的文件主表 id
可以理解为:
- `storage_blob` è´Ÿè´£â€œæ–‡ä»¶æœ¬èº«â€
- `storage_attachment` è´Ÿè´£â€œæ–‡ä»¶æŒ‚在哪条业务数据上”
## 2. ä¸Šä¼ æµç¨‹
### 2.1 æ™®é€šä¸Šä¼ 
接口:
- `POST /common/upload`
控制器位置:
- `src/main/java/com/ruoyi/project/common/CommonController.java`
入参:
- è¡¨å•字段名:`files`
- ç±»åž‹ï¼š`List<MultipartFile>`
代码逻辑:
1. å‰ç«¯å…ˆè°ƒç”¨ `/common/upload`
2. `CommonController.upload()` è°ƒç”¨ `storageBlobService.upload(files, false)`
3. æœåŠ¡å±‚ä¿å­˜æ–‡ä»¶å…ƒæ•°æ®åˆ° `storage_blob`
4. è¿”回 `StorageBlobVO` åˆ—表,里面通常会带:
   - æ–‡ä»¶ id
   - åŽŸå§‹æ–‡ä»¶å
   - å”¯ä¸€æ–‡ä»¶å
   - é¢„览地址 `previewURL`
   - ä¸‹è½½åœ°å€ `downloadURL`
说明:
- æ­¤æ—¶åªæ˜¯â€œä¸Šä¼ äº†æ–‡ä»¶â€
- è¿˜æ²¡æœ‰å’Œå…·ä½“业务单据建立关系
### 2.2 å…¬å…±ä¸Šä¼ 
接口:
- `POST /common/public/upload`
代码逻辑:
- `CommonController.publicUpload()` è°ƒç”¨ `storageBlobService.upload(files, true)`
说明:
- è¯¥æŽ¥å£ä¸Šä¼ çš„æ–‡ä»¶èµ°â€œå…¬å…±æ–‡ä»¶â€æ¨¡å¼
- æŽ§åˆ¶å™¨æ³¨é‡Šå·²æ˜Žç¡®è¯´æ˜Žï¼šæ°¸ä¹…有效,慎用
- å¯¹åº” URL æž„建时,可能走 `publicKey` å‚数,而不是临时 `token`
## 3. é™„件绑定流程
上传完成后,如果需要把文件绑定到某条业务记录,需要再调用附件接口。
接口:
- `POST /storageAttachment/add`
控制器位置:
- `src/main/java/com/ruoyi/basic/controller/StorageAttachmentController.java`
核心请求对象:
- `StorageAttachmentDTO`
其中继承了 `StorageAttachment`,并额外包含:
- `storageBlobDTOs`:待绑定的文件列表
常用字段含义:
- `application`:文件用途
- `recordType`:业务类型
- `recordId`:业务主键
- `storageBlobDTOs[].id`:上传成功后返回的文件 id
示例请求体:
```json
{
  "application": "file",
  "recordType": "common_file",
  "recordId": 1001,
  "storageBlobDTOs": [
    {
      "id": 12,
      "application": "file"
    },
    {
      "id": 13,
      "application": "file"
    }
  ]
}
```
绑定逻辑说明:
1. å…ˆä¸Šä¼ æ–‡ä»¶ï¼Œæ‹¿åˆ° `storage_blob.id`
2. å†è°ƒç”¨ `/storageAttachment/add`
3. æœåŠ¡å±‚æœ€ç»ˆä¼šé€šè¿‡ `FileUtil` ä¿å­˜ `storage_attachment`
4. åŽç»­å³å¯æŒ‰ä¸šåŠ¡è®°å½•æŸ¥è¯¢å‡ºè¯¥è®°å½•ä¸‹çš„é™„ä»¶
## 4. æŸ¥è¯¢ä¸Žåˆ é™¤é™„ä»¶
### 4.1 æŸ¥è¯¢é™„件列表
接口:
- `GET /storageAttachment/list`
说明:
- æŒ‰ `StorageAttachmentDTO` ä¸­çš„æ¡ä»¶æŸ¥è¯¢
- å¸¸è§æ¡ä»¶æ˜¯ `application`、`recordType`、`recordId`
- è¿”回结果本质上是和业务记录关联后的文件列表
### 4.2 åˆ é™¤é™„ä»¶
接口:
- `DELETE /storageAttachment/delete`
请求体:
- `List<Long> ids`
说明:
- è¿™é‡Œçš„ `ids` æ˜¯é™„件关联表 id,一般是 `storage_attachment.id`
- åˆ é™¤æ—¶é€šå¸¸ä¸ä»…会删关联关系,也会进一步删除对应文件记录
## 5. é¢„览与下载流程
### 5.1 ä¸‹è½½æŽ¥å£
接口:
- `GET /common/download/{fileName}`
支持两种访问方式:
- ä¸´æ—¶é“¾æŽ¥ï¼š`token`
- å…¬å…±é“¾æŽ¥ï¼š`publicKey`
代码逻辑:
1. å¦‚果请求里有 `publicKey`,走 `storageBlobService.getPublicFile(fileName, publicKey)`
2. å¦åˆ™èµ° `storageBlobService.getFileByToken(fileName, token)`
3. å–到实际文件后,调用 `fileUtil.compressFile(file)` åšå›¾ç‰‡åŽ‹ç¼©å¤„ç†
4. è®¾ç½®ä¸‹è½½å“åº”头,输出文件流
### 5.2 é¢„览接口
接口:
- `GET /common/preview/{fileName}`
支持两种访问方式:
- ä¸´æ—¶é“¾æŽ¥ï¼š`token`
- å…¬å…±é“¾æŽ¥ï¼š`publicKey`
代码逻辑:
1. æ ¡éªŒ `token` æˆ– `publicKey`
2. èŽ·å–æ–‡ä»¶
3. è°ƒç”¨ `fileUtil.compressFile(file)`
4. æ ¹æ®æ–‡ä»¶å†…容类型返回 inline é¢„览
## 6. æžšä¸¾å«ä¹‰
### 6.1 `ApplicationTypeEnum`
位置:
- `src/main/java/com/ruoyi/basic/enums/ApplicationTypeEnum.java`
当前定义值:
| æžšä¸¾ | type | è¯´æ˜Ž |
|---|---|---|
| `IMAGE` | `image` | å›¾ç‰‡ç±»æ–‡ä»¶ |
| `FILE` | `file` | æ™®é€šæ–‡ä»¶ |
| `AFTER_FILE` | `after_file` | å”®åŽç›¸å…³æ–‡ä»¶ |
| `BEFORE_FILE` | `before_file` | å”®å‰/前置相关文件 |
| `APK` | `apk` | å®‰è£…包文件 |
作用:
- ç”¨äºŽåŒºåˆ†åŒä¸€æ¡ä¸šåŠ¡è®°å½•ä¸‹ï¼Œä¸åŒç”¨é€”çš„æ–‡ä»¶
- `FileUtil` çš„很多查询、删除、保存方法都会用到该字段
### 6.2 `RecordTypeEnum`
位置:
- `src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java`
作用:
- ç”¨äºŽæ ‡è®°æ–‡ä»¶å±žäºŽå“ªç±»ä¸šåŠ¡è®°å½•
- ä¾‹å¦‚质检、采购、客户、售后、台账、通知、设备等模块
- ä¸Šä¼ å®ŒæˆåŽï¼Œé™„件最终通过 `recordType + recordId` å’Œä¸šåŠ¡æ•°æ®å…³è”
说明:
- è¯¥æžšä¸¾å€¼å¾ˆå¤šï¼Œæ–‡æ¡£ä¸é€ä¸ªå±•å¼€
- å®žé™…使用时必须传代码中已定义的 `type` å€¼
- å¦‚:
  - `common_file`
  - `after_sales_service`
  - `quality_inspect`
  - `product`
  - `notice`
## 7. `FileUtil` æ–¹æ³•说明
`FileUtil` æ˜¯æœ¬å¥—文件上传体系的核心工具类,主要负责:
- æ–‡ä»¶ä¸Žä¸šåŠ¡è®°å½•ç»‘å®š
- æ–‡ä»¶ä¸Žé™„件删除
- é™„件查询
- é¢„览/下载地址生成
- token ä½¿ç”¨æ¬¡æ•°æŽ§åˆ¶
- å›¾ç‰‡åŽ‹ç¼©
下面按功能分组说明每个方法。
### 7.1 ä¿å­˜é™„件关系
#### 1. `saveStorageAttachment(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS)`
作用:
- æŒ‰â€œæ–‡ä»¶ç”¨é€” + è®°å½•类型 + è®°å½• id”保存附件关系
逻辑:
1. æ ¡éªŒ `application`、`recordType`、`recordId`
2. å…ˆåˆ é™¤è¿™ç»„业务记录下的旧附件
3. æŠŠæ–°çš„ `storageBlobDTOS` è½¬æˆ `storage_attachment` è®°å½•后批量插入
适用场景:
- æŸæ¡ä¸šåŠ¡æ•°æ®é‡æ–°ä¿å­˜é™„ä»¶ï¼Œæ—§é™„ä»¶æ•´ä½“æ›¿æ¢æˆæ–°é™„ä»¶
#### 2. `saveStorageAttachmentByRecordTypeAndRecordId(String application, RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS)`
作用:
- æŒ‰ `recordType + recordId` ä¿å­˜é™„件关系,`application` å¯æŒ‡å®šï¼Œä¹Ÿå¯ä»Žæ¯ä¸ªæ–‡ä»¶å¯¹è±¡é‡Œè¯»å–
逻辑特点:
- å¦‚æžœ `application == null`,会根据 `storageBlobDTO.application` åˆ†åˆ«åˆ é™¤æ—§å…³ç³»
- å¦‚果附件列表为空,会直接删除该业务记录的附件关系
- æ’入时会自动回填 `application`
适用场景:
- ä¸€æ¬¡æäº¤é‡Œå¯èƒ½åŒ…含多种用途的附件
- æˆ–者调用方不方便直接传枚举类型
### 7.2 åˆ é™¤æ–‡ä»¶ä¸»è¡¨ `storage_blob`
#### 3. `deleteStorageBlobs(List<Long> storageBlobIds)`
作用:
- æŒ‰æ–‡ä»¶ä¸»è¡¨ id æ‰¹é‡åˆ é™¤æ–‡ä»¶è®°å½•
#### 4. `deleteStorageBlobsByStorageAttachmentIds(List<Long> storageAttachmentIds)`
作用:
- å…ˆæ ¹æ®é™„件关联 id æŸ¥åˆ° `storageBlobId`
- å†åˆ é™¤å¯¹åº”的文件主表记录
#### 5. `deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds)`
作用:
- æ ¹æ®ç”¨é€”、记录类型、多个业务 id,批量删除对应的文件主表记录
适用场景:
- æ‰¹é‡åˆ é™¤æŸç±»ä¸šåŠ¡æ•°æ®æ—¶ï¼ŒåŒæ—¶æ¸…ç†é™„ä»¶
#### 6. `deleteStorageBlobsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId)`
作用:
- æ ¹æ® `recordType + recordId` åˆ é™¤è¯¥ä¸šåŠ¡è®°å½•ä¸‹æ‰€æœ‰æ–‡ä»¶ä¸»è¡¨è®°å½•
### 7.3 åˆ é™¤é™„件关系 `storage_attachment`
#### 7. `deleteStorageAttachmentsByStorageAttachmentIds(List<Long> storageAttachmentIds)`
作用:
- å…ˆåˆ é™¤é™„件对应的文件主表记录
- å†åˆ é™¤é™„件关系表记录
#### 8. `deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)`
作用:
- åˆ é™¤æŒ‡å®šç”¨é€”、指定业务记录下的附件关系
特点:
- ä¼šå…ˆåˆ  blob,再删 attachment
#### 9. `deleteStorageAttachmentsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId)`
作用:
- åˆ é™¤æŒ‡å®šä¸šåŠ¡è®°å½•ä¸‹å…¨éƒ¨é™„ä»¶å…³ç³»ï¼Œä¸åŒºåˆ†ç”¨é€”
#### 10. `deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds)`
作用:
- æŒ‰å¤šä¸ªä¸šåŠ¡ id æ‰¹é‡åˆ é™¤é™„件关系
### 7.4 æŸ¥è¯¢é™„件关系
#### 11. `getStorageAttachmentsByStorageAttachmentIds(List<Long> storageAttachmentIds)`
作用:
- æ ¹æ®é™„件关系 id æŸ¥è¯¢ `storage_attachment` è®°å½•
#### 12. `getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)`
作用:
- æŒ‰ç”¨é€”、业务类型、业务 id æŸ¥è¯¢é™„件关系
#### 13. `getStorageAttachmentsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId)`
作用:
- æŒ‰ä¸šåŠ¡ç±»åž‹ã€ä¸šåŠ¡ id æŸ¥è¯¢é™„件关系
### 7.5 æŸ¥è¯¢æ–‡ä»¶ä¿¡æ¯ `StorageBlobVO`
#### 14. `getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(StorageAttachmentDTO storageAttachmentDTO)`
作用:
- é€šè¿‡ `StorageAttachmentDTO` æ¡ä»¶æŸ¥è¯¢æ–‡ä»¶åˆ—表
特点:
- `application` å¯é€‰
- æœ€ç»ˆè¿”回的是带预览/下载地址的 `StorageBlobVO`
#### 15. `getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds)`
作用:
- æ ¹æ®é™„件关系 id æŸ¥è¯¢æ–‡ä»¶åˆ—表
特点:
- ä¼šè‡ªåŠ¨æž„å»ºï¼š
  - `previewURL`
  - `downloadURL`
  - `storageAttachmentId`
#### 16. `getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)`
作用:
- æŒ‰ç”¨é€”、业务类型、业务 id æŸ¥è¯¢æ–‡ä»¶åˆ—表
#### 17. `getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId)`
作用:
- æŒ‰ä¸šåŠ¡ç±»åž‹ã€ä¸šåŠ¡ id æŸ¥è¯¢æ–‡ä»¶åˆ—表
#### 18. `getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired)`
作用:
- å’Œç¬¬ 16 ä¸ªæ–¹æ³•类似,但可以自定义链接过期时间
说明:
- `expired` å•位是分钟
- è¿”回的预览/下载地址会按这个时间生成签名
#### 19. `getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired)`
作用:
- æ ¹æ®é™„件关系 id æŸ¥è¯¢æ–‡ä»¶åˆ—表,并自定义链接过期时间
### 7.6 æŸ¥è¯¢é™„件视图 `StorageAttachmentVO`
#### 20. `getStorageAttachmentVOSByStorageAttachmentIds(List<Long> storageAttachmentIds)`
作用:
- æŸ¥è¯¢é™„件视图对象
特点:
- æ¯æ¡é™„件记录里会嵌套自己的 `storageBlobVOS`
#### 21. `getStorageAttachmentVOSByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired)`
作用:
- æ ¹æ®é™„件关系 id æŸ¥è¯¢é™„件视图,并自定义链接过期时间
#### 22. `getStorageAttachmentVOSByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)`
作用:
- æŒ‰ä¸šåŠ¡ç»´åº¦æŸ¥è¯¢é™„ä»¶è§†å›¾
#### 23. `getStorageAttachmentVOSByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired)`
作用:
- æŒ‰ä¸šåŠ¡ç»´åº¦æŸ¥è¯¢é™„ä»¶è§†å›¾ï¼Œå¹¶è‡ªå®šä¹‰é“¾æŽ¥è¿‡æœŸæ—¶é—´
### 7.7 ä»…获取预览地址
#### 24. `getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds)`
作用:
- æ ¹æ®é™„件关系 id åˆ—表,返回预览地址列表
#### 25. `getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired)`
作用:
- æ ¹æ®é™„件关系 id åˆ—表,返回带自定义过期时间的预览地址列表
#### 26. `getFilePreviewURLByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)`
作用:
- æŒ‰ä¸šåŠ¡ç»´åº¦è¿”å›žé¢„è§ˆåœ°å€åˆ—è¡¨
#### 27. `getFilePreviewURLByApplicationAndRecordTypeAndRecordIdAndExpired(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired)`
作用:
- æŒ‰ä¸šåŠ¡ç»´åº¦è¿”å›žå¸¦è‡ªå®šä¹‰è¿‡æœŸæ—¶é—´çš„é¢„è§ˆåœ°å€åˆ—è¡¨
### 7.8 ä»…获取下载地址
#### 28. `getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds)`
作用:
- æ ¹æ®é™„件关系 id åˆ—表,返回下载地址列表
#### 29. `getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired)`
作用:
- æ ¹æ®é™„件关系 id åˆ—表,返回带自定义过期时间的下载地址列表
#### 30. `getFileDownloadURLByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId)`
作用:
- æŒ‰ä¸šåŠ¡ç»´åº¦è¿”å›žä¸‹è½½åœ°å€åˆ—è¡¨
#### 31. `getFileDownloadURLByApplicationAndRecordTypeAndRecordIdAndExpired(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired)`
作用:
- æŒ‰ä¸šåŠ¡ç»´åº¦è¿”å›žå¸¦è‡ªå®šä¹‰è¿‡æœŸæ—¶é—´çš„ä¸‹è½½åœ°å€åˆ—è¡¨
### 7.9 æž„建签名 URL
#### 32. `buildSignedPreviewUrl(StorageBlobVO storageBlob)`
作用:
- ä½¿ç”¨ç³»ç»Ÿé»˜è®¤è¿‡æœŸæ—¶é—´ï¼Œç”Ÿæˆé¢„览链接
实际调用:
- å†…部等价于调用 `buildSignedUrl(storageBlob, "/preview/", properties.getExpired())`
#### 33. `buildSignedDownloadUrl(StorageBlobVO storageBlob)`
作用:
- ä½¿ç”¨ç³»ç»Ÿé»˜è®¤è¿‡æœŸæ—¶é—´ï¼Œç”Ÿæˆä¸‹è½½é“¾æŽ¥
实际调用:
- å†…部等价于调用 `buildSignedUrl(storageBlob, "/download/", properties.getExpired())`
#### 34. `buildSignedUrl(StorageBlobVO storageBlob, String actionPath, BigDecimal expired)`
作用:
- æž„建统一的带签名预览/下载地址
支持:
- `actionPath = "/preview/"`
- `actionPath = "/download/"`
核心逻辑:
1. æ ¡éªŒè·¯å¾„参数和文件信息
2. æ‹¼æŽ¥åŸºç¡€è®¿é—®åœ°å€
3. å¦‚æžœ `expired == -1`,不生成 token,直接走 `publicKey`
4. å¦åˆ™ç”Ÿæˆå¸¦è¿‡æœŸæ—¶é—´çš„ JWT token
5. æŠŠ token çš„使用次数信息写入 Redis
6. è¿”回最终 URL
重要说明:
- `expired` å•位为分钟
- é»˜è®¤è¿‡æœŸæ—¶é—´ä¸º 120 åˆ†é’Ÿ
- éžæ°¸ä¹…链接会受“过期时间 + ä½¿ç”¨æ¬¡æ•°é™åˆ¶â€åŒé‡æŽ§åˆ¶
### 7.10 token ä½¿ç”¨æŽ§åˆ¶
#### 35. `cacheTokenUsage(String token, long expiredMillis)`
作用:
- æŠŠ token ä½¿ç”¨æ¬¡æ•°åˆå§‹åŒ–到 Redis
特点:
- åˆå§‹å€¼å†™å…¥ä¸º `0`
- TTL ä¸Ž token è¿‡æœŸæ—¶é—´ä¿æŒä¸€è‡´
说明:
- è¿™æ˜¯ç§æœ‰æ–¹æ³•,供 `buildSignedUrl()` å†…部调用
#### 36. `buildTokenUsageKey(String token)`
作用:
- ç»Ÿä¸€ç”Ÿæˆ Redis key
格式:
- `file:token:usage:{token}`
说明:
- è¿™æ˜¯ç§æœ‰æ–¹æ³•
#### 37. `validateTokenUsage(String token)`
作用:
- æ ¡éªŒ token æ˜¯å¦è¿˜èƒ½ç»§ç»­ä½¿ç”¨
核心逻辑:
1. ä»Ž Redis è¯»å–当前使用次数
2. å¦‚果没有值,认为链接已过期或已失效
3. å¦‚果达到上限,立即删除 Redis è®°å½•并报错
4. å¦åˆ™è‡ªå¢žä¸€æ¬¡ä½¿ç”¨æ¬¡æ•°
5. å¦‚果自增后达到上限,再删除 Redis è®°å½•
说明:
- è¯¥æ–¹æ³•通常会在实际访问文件时由服务层调用
#### 38. `resolveLimit()`
作用:
- è§£æž token å¯ä½¿ç”¨æ¬¡æ•°ä¸Šé™
规则:
- `properties.getUseLimit() <= 0` æ—¶ï¼Œé»˜è®¤è¿”回 `10`
说明:
- è¿™æ˜¯ç§æœ‰æ–¹æ³•
### 7.11 è·¯å¾„与压缩
#### 39. `buildRelativePath()`
作用:
- ç”Ÿæˆæ–‡ä»¶å­˜å‚¨ç›¸å¯¹è·¯å¾„
格式:
- `yyyy/MMdd`
例如:
- `2026/0430`
用途:
- ä¸€èˆ¬ç”¨äºŽæŒ‰æ—¥æœŸåˆ†ç›®å½•保存上传文件
#### 40. `compressFile(File file)`
作用:
- å¯¹å›¾ç‰‡è¿›è¡ŒåŽ‹ç¼©ï¼Œéžå›¾ç‰‡æˆ–ä¸æ»¡è¶³æ¡ä»¶æ—¶è¿”å›žåŽŸæ–‡ä»¶
压缩条件:
1. å¼€å¯äº† `properties.getCompress()`
2. æ–‡ä»¶æ˜¯å›¾ç‰‡
3. æ–‡ä»¶å¤§å°å¤§äºŽ `properties.getNeedCompressSize()`
处理逻辑:
1. ç›®æ ‡æ–‡ä»¶åä¸º `thumb_原文件名`
2. å¦‚果压缩文件已存在,直接复用
3. ä½¿ç”¨ `Thumbnailator` æŒ‰åŽŸå°ºå¯¸åŽ‹ç¼©ç”»è´¨
4. å¦‚果压缩失败,降级返回原文件
说明:
- å½“前下载和预览接口都会调用这个方法
#### 41. `isImage(String fileName)`
作用:
- ç®€å•判断文件是否是图片
支持后缀:
- `jpg`
- `jpeg`
- `png`
说明:
- è¿™æ˜¯ç§æœ‰æ–¹æ³•,供 `compressFile()` ä½¿ç”¨
## 8. æŽ¨èä½¿ç”¨é¡ºåº
业务上最常见的接入顺序如下:
1. å‰ç«¯ä¸Šä¼ æ–‡ä»¶åˆ° `/common/upload`
2. æ‹¿åˆ°è¿”回结果中的文件 id
3. ä¸šåŠ¡ä¿å­˜æ—¶è°ƒç”¨ `/storageAttachment/add`
4. ä¼ å…¥ `application + recordType + recordId + storageBlobDTOs`
5. åŽç»­é¡µé¢å›žæ˜¾æ—¶æŒ‰ä¸šåŠ¡æ¡ä»¶è°ƒç”¨é™„ä»¶æŸ¥è¯¢
6. å‰ç«¯ä½¿ç”¨è¿”回的 `previewURL` æˆ– `downloadURL`
## 9. å¸¸è§æ³¨æ„ç‚¹
### 9.1 å…ˆä¸Šä¼ ï¼Œå†ç»‘定
- `/common/upload` åªè´Ÿè´£æ–‡ä»¶å…¥åº“
- `/storageAttachment/add` æ‰æ˜¯å’Œä¸šåŠ¡æ•°æ®å»ºç«‹å…³ç³»
### 9.2 `application` å¾ˆé‡è¦
- åŒä¸€æ¡ `recordId` ä¸‹å¯èƒ½æœ‰å¤šç»„不同用途附件
- åˆ é™¤å’ŒæŸ¥è¯¢æ—¶ï¼Œç»å¸¸ä¾èµ– `application`
### 9.3 ä¸‹è½½é“¾æŽ¥ä¸æ˜¯æ°¸ä¹…有效
- æ™®é€šé“¾æŽ¥ä¸€èˆ¬é€šè¿‡ JWT token æŽ§åˆ¶
- åŒæ—¶å—过期时间和使用次数限制
### 9.4 å…¬å…±æ–‡ä»¶è¦æ…Žç”¨
- `public/upload` ä¸Šä¼ çš„æ–‡ä»¶å¯èµ°æ°¸ä¹…公开访问
- é€‚合公开资源,不适合敏感文件
### 9.5 å›¾ç‰‡é¢„览/下载可能返回压缩文件
- å½“前控制器在下载和预览前都会调用 `compressFile()`
- å¤§å›¾åœ¨è®¿é—®æ—¶å¯èƒ½ä½¿ç”¨åŽ‹ç¼©åŽçš„å‰¯æœ¬
## 10. ä¸€å¥è¯æ€»ç»“
本项目的文件上传方案是“两阶段模型”:
- ç¬¬ä¸€é˜¶æ®µä¸Šä¼ æ–‡ä»¶ï¼Œç”Ÿæˆ `storage_blob`
- ç¬¬äºŒé˜¶æ®µç»‘定业务,生成 `storage_attachment`
而 `FileUtil` åˆ™è´Ÿè´£æŠŠâ€œä¸Šä¼ åŽçš„æ–‡ä»¶â€å˜æˆâ€œå¯æŸ¥è¯¢ã€å¯é¢„览、可下载、可删除、可控时效”的完整附件能力。
src/main/resources/application-dev-pro.yml
@@ -254,8 +254,8 @@
# æ–‡ä»¶ä¸Šä¼ é…ç½®
file:
  temp-dir: D:/ruoyi/temp/uploads   # ä¸´æ—¶ç›®å½•
  upload-dir: D:/ruoyi/prod/uploads # æ­£å¼ç›®å½•
  temp-dir: D:/ruoyi/temp/uploads   # ä¸´æ—¶ç›®å½• åŽæœŸåˆ é™¤
  upload-dir: D:/ruoyi/prod/uploads # æ­£å¼ç›®å½• åŽæœŸåˆ é™¤
  path: C:/Users/12631/Desktop/download/uploads # ä¸Šä¼ ç›®å½•
  urlPrefix: /common # é“¾æŽ¥å‰ç¼€
  domain: http://127.0.0.1:7003 # åŸŸåå‰ç¼€