feat(infra): 文件列表及详情接口增加私有桶预签名
- FileController.getFilePage/getFile 对私有桶文件 URL 生成预签名访问地址 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,137 +1,149 @@
|
|||||||
package com.viewsh.module.infra.controller.admin.file;
|
package com.viewsh.module.infra.controller.admin.file;
|
||||||
|
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.core.util.URLUtil;
|
import cn.hutool.core.util.URLUtil;
|
||||||
import com.viewsh.framework.common.pojo.CommonResult;
|
import com.viewsh.framework.common.pojo.CommonResult;
|
||||||
import com.viewsh.framework.common.pojo.PageResult;
|
import com.viewsh.framework.common.pojo.PageResult;
|
||||||
import com.viewsh.framework.common.util.object.BeanUtils;
|
import com.viewsh.framework.common.util.object.BeanUtils;
|
||||||
import com.viewsh.framework.tenant.core.aop.TenantIgnore;
|
import com.viewsh.framework.tenant.core.aop.TenantIgnore;
|
||||||
import com.viewsh.module.infra.controller.admin.file.vo.file.*;
|
import com.viewsh.module.infra.controller.admin.file.vo.file.*;
|
||||||
import com.viewsh.module.infra.dal.dataobject.file.FileDO;
|
import com.viewsh.module.infra.dal.dataobject.file.FileDO;
|
||||||
import com.viewsh.module.infra.service.file.FileService;
|
import com.viewsh.module.infra.service.file.FileService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.Parameters;
|
import io.swagger.v3.oas.annotations.Parameters;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.annotation.security.PermitAll;
|
import jakarta.annotation.security.PermitAll;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.viewsh.framework.common.pojo.CommonResult.success;
|
import static com.viewsh.framework.common.pojo.CommonResult.success;
|
||||||
import static com.viewsh.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
|
import static com.viewsh.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
|
||||||
|
|
||||||
@Tag(name = "管理后台 - 文件存储")
|
@Tag(name = "管理后台 - 文件存储")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/infra/file")
|
@RequestMapping("/infra/file")
|
||||||
@Validated
|
@Validated
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class FileController {
|
public class FileController {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private FileService fileService;
|
private FileService fileService;
|
||||||
|
|
||||||
@PostMapping("/upload")
|
@PostMapping("/upload")
|
||||||
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
|
@Operation(summary = "上传文件", description = "模式一:后端上传文件")
|
||||||
@Parameter(name = "file", description = "文件附件", required = true,
|
@Parameter(name = "file", description = "文件附件", required = true,
|
||||||
schema = @Schema(type = "string", format = "binary"))
|
schema = @Schema(type = "string", format = "binary"))
|
||||||
public CommonResult<String> uploadFile(@Valid FileUploadReqVO uploadReqVO) throws Exception {
|
public CommonResult<String> uploadFile(@Valid FileUploadReqVO uploadReqVO) throws Exception {
|
||||||
MultipartFile file = uploadReqVO.getFile();
|
MultipartFile file = uploadReqVO.getFile();
|
||||||
byte[] content = IoUtil.readBytes(file.getInputStream());
|
byte[] content = IoUtil.readBytes(file.getInputStream());
|
||||||
return success(fileService.createFile(content, file.getOriginalFilename(),
|
return success(fileService.createFile(content, file.getOriginalFilename(),
|
||||||
uploadReqVO.getDirectory(), file.getContentType()));
|
uploadReqVO.getDirectory(), file.getContentType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/presigned-url")
|
@GetMapping("/presigned-url")
|
||||||
@Operation(summary = "获取文件预签名地址(上传)", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器")
|
@Operation(summary = "获取文件预签名地址(上传)", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器")
|
||||||
@Parameters({
|
@Parameters({
|
||||||
@Parameter(name = "name", description = "文件名称", required = true),
|
@Parameter(name = "name", description = "文件名称", required = true),
|
||||||
@Parameter(name = "directory", description = "文件目录")
|
@Parameter(name = "directory", description = "文件目录")
|
||||||
})
|
})
|
||||||
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(
|
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(
|
||||||
@RequestParam("name") String name,
|
@RequestParam("name") String name,
|
||||||
@RequestParam(value = "directory", required = false) String directory) {
|
@RequestParam(value = "directory", required = false) String directory) {
|
||||||
return success(fileService.presignPutUrl(name, directory));
|
return success(fileService.presignPutUrl(name, directory));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
@Operation(summary = "创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件")
|
@Operation(summary = "创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件")
|
||||||
public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
|
public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
|
||||||
return success(fileService.createFile(createReqVO));
|
return success(fileService.createFile(createReqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/get")
|
@GetMapping("/get")
|
||||||
@Operation(summary = "获得文件")
|
@Operation(summary = "获得文件")
|
||||||
@Parameter(name = "id", description = "编号", required = true)
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
@PreAuthorize("@ss.hasPermission('infra:file:query')")
|
@PreAuthorize("@ss.hasPermission('infra:file:query')")
|
||||||
public CommonResult<FileRespVO> getFile(@RequestParam("id") Long id) {
|
public CommonResult<FileRespVO> getFile(@RequestParam("id") Long id) {
|
||||||
return success(BeanUtils.toBean(fileService.getFile(id), FileRespVO.class));
|
FileRespVO respVO = BeanUtils.toBean(fileService.getFile(id), FileRespVO.class);
|
||||||
}
|
// 私有桶:对文件 URL 生成预签名访问地址
|
||||||
|
if (respVO != null && StrUtil.isNotEmpty(respVO.getUrl())) {
|
||||||
@DeleteMapping("/delete")
|
respVO.setUrl(fileService.presignGetUrl(respVO.getUrl(), null));
|
||||||
@Operation(summary = "删除文件")
|
}
|
||||||
@Parameter(name = "id", description = "编号", required = true)
|
return success(respVO);
|
||||||
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
|
}
|
||||||
public CommonResult<Boolean> deleteFile(@RequestParam("id") Long id) throws Exception {
|
|
||||||
fileService.deleteFile(id);
|
@DeleteMapping("/delete")
|
||||||
return success(true);
|
@Operation(summary = "删除文件")
|
||||||
}
|
@Parameter(name = "id", description = "编号", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
|
||||||
@DeleteMapping("/delete-list")
|
public CommonResult<Boolean> deleteFile(@RequestParam("id") Long id) throws Exception {
|
||||||
@Operation(summary = "批量删除文件")
|
fileService.deleteFile(id);
|
||||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
return success(true);
|
||||||
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
|
}
|
||||||
public CommonResult<Boolean> deleteFileList(@RequestParam("ids") List<Long> ids) throws Exception {
|
|
||||||
fileService.deleteFileList(ids);
|
@DeleteMapping("/delete-list")
|
||||||
return success(true);
|
@Operation(summary = "批量删除文件")
|
||||||
}
|
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
|
||||||
@GetMapping("/{configId}/get/**")
|
public CommonResult<Boolean> deleteFileList(@RequestParam("ids") List<Long> ids) throws Exception {
|
||||||
@PermitAll
|
fileService.deleteFileList(ids);
|
||||||
@TenantIgnore
|
return success(true);
|
||||||
@Operation(summary = "下载文件")
|
}
|
||||||
@Parameter(name = "configId", description = "配置编号", required = true)
|
|
||||||
public void getFileContent(HttpServletRequest request,
|
@GetMapping("/{configId}/get/**")
|
||||||
HttpServletResponse response,
|
@PermitAll
|
||||||
@PathVariable("configId") Long configId) throws Exception {
|
@TenantIgnore
|
||||||
// 获取请求的路径
|
@Operation(summary = "下载文件")
|
||||||
String path = StrUtil.subAfter(request.getRequestURI(), "/get/", false);
|
@Parameter(name = "configId", description = "配置编号", required = true)
|
||||||
if (StrUtil.isEmpty(path)) {
|
public void getFileContent(HttpServletRequest request,
|
||||||
throw new IllegalArgumentException("结尾的 path 路径必须传递");
|
HttpServletResponse response,
|
||||||
}
|
@PathVariable("configId") Long configId) throws Exception {
|
||||||
// 解码,解决中文路径的问题
|
// 获取请求的路径
|
||||||
// https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/
|
String path = StrUtil.subAfter(request.getRequestURI(), "/get/", false);
|
||||||
// https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1432/
|
if (StrUtil.isEmpty(path)) {
|
||||||
path = URLUtil.decode(path, StandardCharsets.UTF_8, false);
|
throw new IllegalArgumentException("结尾的 path 路径必须传递");
|
||||||
|
}
|
||||||
// 读取内容
|
// 解码,解决中文路径的问题
|
||||||
byte[] content = fileService.getFileContent(configId, path);
|
// https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/
|
||||||
if (content == null) {
|
// https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1432/
|
||||||
log.warn("[getFileContent][configId({}) path({}) 文件不存在]", configId, path);
|
path = URLUtil.decode(path, StandardCharsets.UTF_8, false);
|
||||||
response.setStatus(HttpStatus.NOT_FOUND.value());
|
|
||||||
return;
|
// 读取内容
|
||||||
}
|
byte[] content = fileService.getFileContent(configId, path);
|
||||||
writeAttachment(response, path, content);
|
if (content == null) {
|
||||||
}
|
log.warn("[getFileContent][configId({}) path({}) 文件不存在]", configId, path);
|
||||||
|
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||||
@GetMapping("/page")
|
return;
|
||||||
@Operation(summary = "获得文件分页")
|
}
|
||||||
@PreAuthorize("@ss.hasPermission('infra:file:query')")
|
writeAttachment(response, path, content);
|
||||||
public CommonResult<PageResult<FileRespVO>> getFilePage(@Valid FilePageReqVO pageVO) {
|
}
|
||||||
PageResult<FileDO> pageResult = fileService.getFilePage(pageVO);
|
|
||||||
return success(BeanUtils.toBean(pageResult, FileRespVO.class));
|
@GetMapping("/page")
|
||||||
}
|
@Operation(summary = "获得文件分页")
|
||||||
|
@PreAuthorize("@ss.hasPermission('infra:file:query')")
|
||||||
}
|
public CommonResult<PageResult<FileRespVO>> getFilePage(@Valid FilePageReqVO pageVO) {
|
||||||
|
PageResult<FileDO> pageResult = fileService.getFilePage(pageVO);
|
||||||
|
PageResult<FileRespVO> voPageResult = BeanUtils.toBean(pageResult, FileRespVO.class);
|
||||||
|
// 私有桶:对文件 URL 生成预签名访问地址
|
||||||
|
voPageResult.getList().forEach(vo -> {
|
||||||
|
if (StrUtil.isNotEmpty(vo.getUrl())) {
|
||||||
|
vo.setUrl(fileService.presignGetUrl(vo.getUrl(), null));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return success(voPageResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user