Browse Source

feat: 更新文档

master
yuanjs@qutke.com 2 weeks ago
parent
commit
2d1c7934f5
  1. 292
      README.md
  2. 12
      src/main/java/org/ycloud/aipan/config/AmazonS3Config.java
  3. 6
      src/main/java/org/ycloud/aipan/enums/BizCodeEnum.java
  4. 64
      src/main/java/org/ycloud/aipan/service/impl/AccountFileServiceImpl.java
  5. 16
      src/main/resources/application.yml
  6. 4
      src/test/java/org/ycloud/aipan/AmazonS3ClientTests.java

292
README.md

@ -4031,21 +4031,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第6集 AWS-S3大文件上传API测试合并分片实战
**简介: AWS-S3大文件上传API测试合并分片实战**
#### AWS-S3大文件上传API测试合并分片实战
* 需求:测试下面相关API
@ -4059,7 +4045,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 测试实战
```
```java
// 测试合并分片的方法
@Test
public void testMergeChunks() {
@ -4101,15 +4087,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第7集 AWS-S3大文件上传API测试上传进度实战
**简介: AWS-S3大文件上传API测试上传进度实战**
#### AWS-S3大文件上传API测试上传进度实战
* 需求:测试下面相关API
@ -4123,7 +4101,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 测试实战
```
```java
@Test
// 测试列出分片上传的各个分片信息
public void testListParts() {
@ -4160,18 +4138,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
```
#### 第8集 存储引擎StoreEngine大文件上传API封装
**简介: 存储引擎StoreEngine大文件上传API封装**
#### 存储引擎StoreEngine大文件上传API封装
* 需求
@ -4181,7 +4148,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 接口
```
```java
/**
* 查询分片数据
* @param bucketName 存储桶名称
@ -4225,7 +4192,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 实现
```
```java
@Override
public PartListing listMultipart(String bucketName, String objectKey, String uploadId) {
try {
@ -4278,27 +4245,11 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
```
### 大文件上传接口开发和全链路测试
![logo](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"**
**更多高级课程请访问 xdclass.net**
### 第十四章 大文件上传接口开发和全链路测试
#### 第1集 初始化分片上传任务接口设计和开发实战
**简介: 初始化分片上传任务接口设计和开发实战**
#### 初始化分片上传任务接口设计和开发实战
* 需求
@ -4307,7 +4258,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 数据库文件分片信息表说明
```
```java
CREATE TABLE `file_chunk` (
`id` bigint NOT NULL,
`identifier` varchar(500) NOT NULL COMMENT '文件唯一标识(md5)',
@ -4336,7 +4287,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 编码实战
```
```java
@Override
@Transactional(rollbackFor = Exception.class)
public FileChunkDTO initFileChunkTask(FileChunkInitTaskReq req) {
@ -4376,25 +4327,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第2集 临时分片上传地址接口设计和开发实战
**简介: 临时分片上传地址接口设计和开发实战**
#### 临时分片上传地址接口设计和开发实战
* 需求
@ -4408,7 +4341,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 编码实战
```
```java
@Override
public String genPreSignUploadUrl(Long accountId, String identifier, Integer partNumber) {
FileChunkDO task = fileChunkMapper.selectOne(new QueryWrapper<FileChunkDO>().lambda().eq(FileChunkDO::getIdentifier, identifier).eq(FileChunkDO::getAccountId, accountId));
@ -4429,25 +4362,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第3集 合并分片文件接口设计和开发实战
**简介: 合并分片文件接口设计和开发实战**
#### 合并分片文件接口设计和开发实战
* 需求
@ -4464,7 +4379,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 编码实战
```
```java
public void mergeFileChunk(FileChunkMergeReq req) {
//获取任务和分片列表,检查是否足够合并
@ -4522,21 +4437,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第4集 查询分片上传进度接口设计和开发实战
**简介: 查询分片上传进度接口设计和开发实战**
#### 查询分片上传进度接口设计和开发实战
* 需求
@ -4550,7 +4451,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 编码实战
```
```java
@Override
public FileChunkDTO listFileChunk(Long accountId, String identifier) {
@ -4579,27 +4480,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第5集 大文件分片上传全链路测试方案设计
**简介: 大文件分片上传全链路测试方案设计**
#### 大文件分片上传全链路测试方案设计
* 需求
@ -4607,8 +4488,6 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 缺少前端的情况下,大文件如何分片进行测试?
* 如何测试大文件上传相关接口?
<img src="D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250124102709550.png" alt="image-20250124102709550" style="zoom:30%;" />
* 测试思路
* 使用后端Java代码进行文件读取
@ -4626,7 +4505,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 使用后端Java代码进行文件读取
* 根据进行切割成不同的小文件
```
```java
SpringBootTest
@Slf4j
class FileChunkUploadTests {
@ -4690,25 +4569,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第6集 大文件分片上传全链路测试代码实战《上》
**简介: 大文件分片上传后端全链路测试方案实战**
#### 大文件分片上传全链路测试代码实战《上》
* 需求
@ -4720,7 +4581,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 编码实战
```
```java
/**
* 第1步,创建分片上传任务
*/
@ -4794,29 +4655,11 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第7集 大文件分片上传全链路测试代码实战《下》
**简介: 大文件分片上传后端全链路测试方案实战**
#### 大文件分片上传全链路测试代码实战《下》
* 需求
* 测试大文件上传全链路代码
<img src="D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250124153127049.png" alt="image-20250124153127049" style="zoom:60%;" />
* 测试实战
* 检查分片文件是否生成
* 检查MinIO是否有对应的文件
@ -4827,26 +4670,11 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
### 网盘文件分享模块设计和开发实战
![logo](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"**
**更多高级课程请访问 xdclass.net**
### 第十五章 网盘文件分享模块设计和开发实战
#### 第1集 文件分享转存需求背景和数据库表说明
**简介: 文件分享转存需求背景和数据库表说明**
#### 文件分享转存需求背景和数据库表说明
* 需求
* 老王有几十G《冰冰的游泳视频》存储在小滴网盘上面
@ -4872,17 +4700,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第2集 文件分享枚举定义和分享列表接口开发
**简介: 文件分享枚举定义和分享列表接口开发**
#### 文件分享枚举定义和分享列表接口开发
* 需求
@ -4893,7 +4711,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 提取码枚举
```
```java
@Getter
public enum ShareTypeEnum {
@ -4911,7 +4729,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 分享状态
```
```java
/**
* 分享状态 used正常, expired已失效, cancled取消
*/
@ -4924,7 +4742,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 分享链接时效性枚举
```
```java
//分享类型(0 永久有效;1: 7天有效;2: 30天有效)
@AllArgsConstructor
@Getter
@ -4957,7 +4775,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 我的分享列表接口
```
```java
@Override
public List<ShareDTO> listShare() {
Long accountId = LoginInterceptor.threadLocal.get().getId();
@ -4970,18 +4788,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第3集 AI智能化云盘-创建文件分享接口开发实战
**简介: AI智能化云盘-创建文件分享接口开发实战**
#### AI智能化云盘-创建文件分享接口开发实战
* 需求
@ -4998,7 +4805,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 编码实战
```
```java
@Override
@Transactional(rollbackFor = Exception.class)
public ShareDTO createShare(ShareCreateReq req) {
@ -5055,21 +4862,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第4集 批量取消分享接口实战和文件移动Bug修复
**简介: 批量取消分享接口实战和文件移动Bug修复**
#### 批量取消分享接口实战和文件移动Bug修复
* 需求
@ -5085,7 +4878,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 编码实战
```
```java
@Transactional(rollbackFor = Exception.class)
public void cancelShare(ShareCancelReq req) {
//1、校验分享ID列表权限是否合格
@ -5108,7 +4901,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 文件移动Bug修复(几个细心的同学发现了Bug,多数同学没发现)
```
```java
@Override
@Transactional(rollbackFor = Exception.class)
public void moveBatch(FileBatchReq req) {
@ -5134,17 +4927,7 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
#### 第5集 链路接口测试-创建-查看-取消分享接口
**简介: 链路接口测试-创建-查看-取消分享接口**
#### 链路接口测试-创建-查看-取消分享接口
* 接口测试需求
* 测试创建文件分享链接接口
@ -5153,8 +4936,6 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
* 测试实战
<img src="D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20250210121918635.png" alt="image-20250210121918635" style="zoom:50%;" />
@ -5184,9 +4965,6 @@ public JsonData list(@RequestParam(value = "parent_id")Long parentId){
![logo](D:/学习/2025/AI智能化云盘学习笔记/笔记/img/image-20230918114907133-5008948.png) **愿景:"IT路上的持续充电平台,让技术不再难学"**
**更多高级课程请访问 xdclass.net**
### 第十六章 自定义注解+AOP实战文件分享设计实战

12
src/main/java/org/ycloud/aipan/config/AmazonS3Config.java

@ -21,10 +21,10 @@ import org.springframework.context.annotation.Configuration;
public class AmazonS3Config {
// 注入Minio配置类,用于获取访问密钥和Endpoint等信息
// @Resource
// private MinioConfig minioConfig;
@Resource
private AliOssConfig aliOssConfig;
private MinioConfig minioConfig;
// @Resource
// private AliOssConfig aliOssConfig;
/**
* 创建并配置Amazon S3客户端
@ -41,16 +41,16 @@ public class AmazonS3Config {
config.setConnectionTimeout(5000);
config.setUseExpectContinue(true);
// 使用配置中的访问密钥和秘密密钥创建AWS凭证
AWSCredentials credentials = new BasicAWSCredentials(aliOssConfig.getAccessKey(), aliOssConfig.getAccessSecret());
AWSCredentials credentials = new BasicAWSCredentials(minioConfig.getAccessKey(), minioConfig.getAccessSecret());
// 设置Endpoint
AwsClientBuilder.EndpointConfiguration endpointConfiguration = new AwsClientBuilder
.EndpointConfiguration(aliOssConfig.getEndpoint(), Regions.US_EAST_1.name());
.EndpointConfiguration(minioConfig.getEndpoint(), Regions.US_EAST_1.name());
// 使用以上配置创建并返回Amazon S3客户端实例
return AmazonS3ClientBuilder.standard()
.withClientConfiguration(config)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withEndpointConfiguration(endpointConfiguration)
.withPathStyleAccessEnabled(false) // mino设置为true,oss设置为false
.withPathStyleAccessEnabled(true) // mino设置为true,oss设置为false
.build();

6
src/main/java/org/ycloud/aipan/enums/BizCodeEnum.java

@ -31,7 +31,11 @@ public enum BizCodeEnum {
SHARE_CANCEL(260406, "分享已取消"),
SHARE_EXPIRED(260407, "分享已过期"),
SHARE_FILE_ILLEGAL(260408, "分享的文件不合规"),
FILE_BATCH_UPDATE_ERROR(270101,"文件批量操作错误" );
FILE_BATCH_UPDATE_ERROR(270101,"文件批量操作错误" ),
FILE_DIR_NOT_EXIST(270102,"用户的上级目录不存在" ),
FILE_DIR_ERROR(270103,"用户的上级目录错误" ),
;
private final int code;
private final String message;

64
src/main/java/org/ycloud/aipan/service/impl/AccountFileServiceImpl.java

@ -58,7 +58,32 @@ public class AccountFileServiceImpl implements AccountFileService {
@Override
public Long createFolder(FolderCreateReq req) {
AccountFileDTO accountFileDTO = AccountFileDTO.builder().accountId(req.getAccountId())
// 创建文件夹时,父ID必须是该用户下的
if (req.getParentId() != null) {
if (req.getParentId() == 0L) {
// 用户根目录不能操作
throw new BizException(BizCodeEnum.FILE_DIR_NOT_EXIST);
}
// 校验该用户下是否存在该父级id
Long sum = accountFileMapper.selectCount(new QueryWrapper<AccountFileDO>()
.eq("id", req.getParentId()).eq("account_id", req.getAccountId()));
if (sum == 0) {
throw new BizException(BizCodeEnum.FILE_DIR_NOT_EXIST);
}
if (sum > 1) {
throw new BizException(BizCodeEnum.FILE_DIR_ERROR);
}
} else {
AccountFileDO fileUser = accountFileMapper.selectOne(new QueryWrapper<AccountFileDO>()
.eq("parent_id", 0L).eq("account_id", req.getAccountId()));
if (fileUser == null) {
// 用户根目录不存在
throw new BizException(BizCodeEnum.FILE_DIR_ERROR);
}
req.setParentId(fileUser.getId());
}
AccountFileDTO accountFileDTO = AccountFileDTO.builder()
.accountId(req.getAccountId())
.parentId(req.getParentId())
.fileName(req.getFolderName())
.isDir(FolderFlagEnum.YES.getCode()).build();
@ -106,7 +131,7 @@ public class AccountFileServiceImpl implements AccountFileService {
.eq("is_dir", FolderFlagEnum.YES.getCode())
);
if(CollectionUtils.isEmpty(folderList)){
if (CollectionUtils.isEmpty(folderList)) {
return List.of();
}
//构建一个map, key是文件ID,value是文件对象 相当于一个数据源
@ -123,7 +148,7 @@ public class AccountFileServiceImpl implements AccountFileService {
//构建文件树,遍历数据源,为每个文件夹找到子文件夹
for (FolderTreeNodeDTO node : folderMap.values()) {
Long parentId = node.getParentId();
if(parentId!=null && folderMap.containsKey(parentId)){
if (parentId != null && folderMap.containsKey(parentId)) {
//获取父文件
FolderTreeNodeDTO parentNode = folderMap.get(parentId);
//获取父文件夹的子节点位置
@ -141,14 +166,16 @@ public class AccountFileServiceImpl implements AccountFileService {
return rootFolderList;
}
/**
* 查询文件树接口 非递归方式
* 1查询用户全部文件夹
* 2拼装文件树
*
* @param accountId
* @return
*/
//@Override
//@Override
public List<FolderTreeNodeDTO> folderTreeV2(Long accountId) {
//查询用户全部文件夹
List<AccountFileDO> folderList = accountFileMapper.selectList(new QueryWrapper<AccountFileDO>()
@ -156,7 +183,7 @@ public class AccountFileServiceImpl implements AccountFileService {
.eq("is_dir", FolderFlagEnum.YES.getCode())
);
if(CollectionUtils.isEmpty(folderList)){
if (CollectionUtils.isEmpty(folderList)) {
return List.of();
}
@ -172,10 +199,10 @@ public class AccountFileServiceImpl implements AccountFileService {
.stream().collect(Collectors.groupingBy(FolderTreeNodeDTO::getParentId));
//处理拼装文件树
for(FolderTreeNodeDTO node : folderTreeNodeDTOList){
for (FolderTreeNodeDTO node : folderTreeNodeDTOList) {
List<FolderTreeNodeDTO> children = folderMap.get(node.getId());
//判断是否为空
if(!CollectionUtils.isEmpty(children)){
if (!CollectionUtils.isEmpty(children)) {
node.getChildren().addAll(children);
}
}
@ -199,7 +226,7 @@ public class AccountFileServiceImpl implements AccountFileService {
//上传到存储引擎
String storeFileObjectKey = storeFile(req);
//保存文件关系 + 保存账号和文件的关系
saveFileAndAccountFile( req,storeFileObjectKey);
saveFileAndAccountFile(req, storeFileObjectKey);
}
/**
@ -214,7 +241,7 @@ public class AccountFileServiceImpl implements AccountFileService {
public void moveBatch(FileBatchReq req) {
// 检查被移动的文件ID是否合法
List<AccountFileDO> accountFileDOList = checkFileIdLegal(req.getFileIds(),req.getAccountId());
List<AccountFileDO> accountFileDOList = checkFileIdLegal(req.getFileIds(), req.getAccountId());
//检查目标文件夹ID是否合法,包括子文件夹
checkTargetParentIdLegal(req);
@ -224,10 +251,10 @@ public class AccountFileServiceImpl implements AccountFileService {
//更新文件或者文件夹的parent_id为目标文件夹的ID
UpdateWrapper<AccountFileDO> updateWrapper = new UpdateWrapper<>();
updateWrapper.in("id",req.getFileIds())
.set("parent_id",req.getTargetParentId());
updateWrapper.in("id", req.getFileIds())
.set("parent_id", req.getTargetParentId());
int updateCount = accountFileMapper.update(null, updateWrapper);
if(updateCount!=req.getFileIds().size()){
if (updateCount != req.getFileIds().size()) {
throw new BizException(BizCodeEnum.FILE_BATCH_UPDATE_ERROR);
}
@ -238,6 +265,7 @@ public class AccountFileServiceImpl implements AccountFileService {
* 检查目标文件夹ID是否合法,包括子文件夹
* 1目标的文件ID不能是文件
* 2要操作的文件列表不能包括目标文件ID
*
* @param req
*/
private void checkTargetParentIdLegal(FileBatchReq req) {
@ -251,8 +279,10 @@ public class AccountFileServiceImpl implements AccountFileService {
throw new BizException(BizCodeEnum.FILE_TARGET_PARENT_ILLEGAL);
}
}
/**
* 检查被移动的文件ID是否合法
*
* @param fileIds
* @param accountId
* @return
@ -262,7 +292,7 @@ public class AccountFileServiceImpl implements AccountFileService {
List<AccountFileDO> accountFileDOList = accountFileMapper
.selectList(new QueryWrapper<AccountFileDO>().in("id", fileIds).eq("account_id", accountId));
if(accountFileDOList.size()!=fileIds.size()){
if (accountFileDOList.size() != fileIds.size()) {
log.error("文件ID数量不合法,ids={}", fileIds);
throw new BizException(BizCodeEnum.FILE_BATCH_UPDATE_ERROR);
}
@ -273,12 +303,13 @@ public class AccountFileServiceImpl implements AccountFileService {
/**
* 保存文件和账号文件的关系到数据库
*
* @param req
* @param storeFileObjectKey
*/
public void saveFileAndAccountFile(FileUploadReq req, String storeFileObjectKey) {
//保存文件
FileDO fileDO = saveFile(req,storeFileObjectKey);
FileDO fileDO = saveFile(req, storeFileObjectKey);
//保存文件账号关系
AccountFileDTO accountFileDTO = AccountFileDTO.builder()
@ -299,7 +330,7 @@ public class AccountFileServiceImpl implements AccountFileService {
FileDO fileDO = new FileDO();
fileDO.setAccountId(req.getAccountId());
fileDO.setFileName(req.getFilename());
fileDO.setFileSize(req.getFile() !=null ? req.getFile().getSize():req.getFileSize());
fileDO.setFileSize(req.getFile() != null ? req.getFile().getSize() : req.getFileSize());
fileDO.setFileSuffix(CommonUtil.getFileSuffix(req.getFilename()));
fileDO.setObjectKey(storeFileObjectKey);
fileDO.setIdentifier(req.getIdentifier());
@ -309,15 +340,16 @@ public class AccountFileServiceImpl implements AccountFileService {
/**
* 上传文件到存储引擎返回存储的文件路径
*
* @param req
* @return
*/
private String storeFile(FileUploadReq req) {
String objectKey = CommonUtil.getFilePath(req.getFilename());
fileStoreEngine.upload(minioConfig.getBucketName(), objectKey, req.getFile());
return objectKey;
}
/**
* 处理用户和文件的关系存储文件和文件夹都是可以的
* <p>

16
src/main/resources/application.yml

@ -4,12 +4,16 @@ server:
spring:
application:
name: ycloud-aipan
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://47.98.137.138:3306/ycloud-aipan?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
url: jdbc:mysql://117.72.43.105:3306/ycloud-aipan?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: Yuan625621105
password: Yuan625621105.
data:
redis:
@ -40,10 +44,12 @@ mybatis-plus:
# minio配置
minio:
endpoint: http://192.168.60.124:9000
access-key: minio_root
access-secret: minio_123456
endpoint: http://106.75.254.18:9000
access-key: yuanjs
access-secret: yuanjs625621105
# bucket需要创建,程序启动后不会自动创建
bucket-name: ai-pan
# 头像要使返回的url直接可访问,需要再创建bucket后设置访问策略为public
avatar-bucket-name: avatar

4
src/test/java/org/ycloud/aipan/AmazonS3ClientTests.java

@ -51,7 +51,7 @@ class AmazonS3ClientTests {
*/
@Test
public void testDeleteBucket() {
String bucketName = "tjcz-app";
String bucketName = "forward-tech";
amazonS3Client.deleteBucket(bucketName);
}
@ -93,7 +93,7 @@ class AmazonS3ClientTests {
*/
@Test
public void testUploadFile2() {
amazonS3Client.putObject("ai-pan", "test2.txt", new File("/Users/xdclass/Desktop/dpan.sql"));
amazonS3Client.putObject("ai-pan", "test2.txt", new File("D:\\IdeaProjects\\learn\\ycloud-aipan\\doc\\ycloud-pan.sql"));
}
/**

Loading…
Cancel
Save