feat: preview file

This commit is contained in:
zzs 2025-02-27 10:59:19 +08:00
parent fa867dbc27
commit 335b8dfce5
11 changed files with 323 additions and 3 deletions

View File

@ -79,6 +79,7 @@ public class AccessLogFilter implements GlobalFilter, Ordered {
values.put("userType", gatewayLog.getUserType());
values.put("routeId", gatewayLog.getRoute() != null ? gatewayLog.getRoute().getId() : null);
values.put("schema", gatewayLog.getSchema());
values.put("requestMethod", gatewayLog.getRequestMethod());
values.put("requestUrl", gatewayLog.getRequestUrl());
values.put("queryParams", gatewayLog.getQueryParams().toSingleValueMap());
values.put("requestBody", JsonUtils.isJson(gatewayLog.getRequestBody()) ? // 保证 body 的展示好看

View File

@ -110,4 +110,4 @@ spring:
# 芋道配置项,设置当前项目所有自定义的配置
wmyun:
demo: true # 开启演示模式
demo: false # 开启演示模式

View File

@ -0,0 +1,48 @@
package com.wmyun.module.infra.controller.admin.file;
import com.wmyun.module.infra.controller.admin.file.vo.preview.CheckFileInfoVo;
import com.wmyun.module.infra.service.file.FilePreviewService;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.InputStream;
import static com.wmyun.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
/**
* @Description: 文件预览Wopi
* @Date: 2025/2/26 18:18
* @Created: by ZZSLL
*/
@Tag(name = "管理后台 - 文件预览")
@RestController
@RequestMapping("/infra/file/preview/wopi")
public class OfficePreviewController {
@Autowired
private FilePreviewService previewService;
@GetMapping("/files/{fileId}")
@PermitAll
public ResponseEntity<CheckFileInfoVo> getFileInfo(@PathVariable("fileId") String fileId) {
return ResponseEntity.ok().body(previewService.createPreviewInfo(fileId));
}
@GetMapping(value = "/files/{fileId}/contents")
@PermitAll
public void getFileContents(@PathVariable String fileId, HttpServletResponse response) {
previewService.getFileContent(fileId, response);
}
@RequestMapping(value = "/files/{fileId}/contents", method = {RequestMethod.POST, RequestMethod.PUT})
@PermitAll
public ResponseEntity<?> putFileContents(@PathVariable String fileId, InputStream contentStream) {
previewService.saveFile(fileId, contentStream);
return ResponseEntity.ok().build();
}
}

View File

@ -0,0 +1,100 @@
package com.wmyun.module.infra.controller.admin.file.vo.preview;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
/**
* @Description: wopi server文件信息
* @link <a href="https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/files/checkfileinfo/checkfileinfo-response">https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/files/checkfileinfo/checkfileinfo-response<a/>
* @link <a href="https://learn.microsoft.com/en-us/openspecs/office_protocols/ms-wopi/71e66fb4-144b-4369-b597-f425f0b700b9?redirectedfrom=MSDN">https://learn.microsoft.com/en-us/openspecs/office_protocols/ms-wopi/71e66fb4-144b-4369-b597-f425f0b700b9?redirectedfrom=MSDN<a/>
* @Date: 2025/2/27 9:36
* @Created: by ZZSLL
*/
@Data
@Builder
public class CheckFileInfoVo {
/**
* 基本信息
*/
@Schema(description = "文件名称")
@JsonProperty("BaseFileName")
private String baseFileName;
@Schema(description = "文件创建认")
@JsonProperty("OwnerId")
private String ownerId;
@Schema(description = "文件大小")
@JsonProperty("Size")
private Long size;
@Schema(description = "当前用户id")
@JsonProperty("UserId")
private String userId;
@Schema(description = "用户昵称")
@JsonProperty("UserFriendlyName")
private String userFriendlyName;
@Schema(description = "版本号")
@JsonProperty("Version")
private String version;
/**
* WOPI host capabilities properties
*/
@Schema(description = "支持删除文档")
@JsonProperty("SupportsDeleteFile")
private Boolean supportsDeleteFile;
@Schema(description = "支持文件重命名")
@JsonProperty("SupportsRename")
private Boolean supportsRename;
@Schema(description = "支持更新文件")
@JsonProperty("SupportsUpdate")
private Boolean supportsUpdate;
/**
* https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/files/checkfileinfo/checkfileinfo-response#userinfo
*/
@Schema(description = "支持获取用户信息")
@JsonProperty("SupportsUserInfo")
private Boolean supportsUserInfo;
/**
* User permissions properties
*/
@Schema(description = "只读")
@JsonProperty("ReadOnly")
private Boolean readOnly;
@Schema(description = "重命名")
@JsonProperty("UserCanRename")
private Boolean userCanRename;
@Schema(description = "更新文件")
@JsonProperty("UserCanWrite")
private Boolean userCanWrite;
/**
* File URL properties
*/
@Schema(description = "文件下载地址")
@JsonProperty("DownloadUrl")
private String downloadUrl;
@Schema(description = "文件下载地址可代替wopi中的putfile")
@JsonProperty("FileUrl")
private String fileUrl;
@Schema(description = "HOST地址")
@JsonProperty("HostViewUrl")
private String hostViewUrl;
}

View File

@ -0,0 +1,27 @@
package com.wmyun.module.infra.controller.admin.file.vo.preview;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import java.nio.file.Path;
/**
* @Description: TODO
* @Date: 2025/2/19 16:56
* @Created: by ZZSLL
*/
@Getter
@Builder
public class WopiFileVo {
private String fileId;
private String fileName;
private long size;
private String ownerId;
private Path filePath;
private boolean userCanWrite;
private boolean supportsLocks;
private String contentType;
}

View File

@ -0,0 +1,25 @@
package com.wmyun.module.infra.service.file;
import com.wmyun.module.infra.controller.admin.file.vo.preview.CheckFileInfoVo;
import com.wmyun.module.infra.controller.admin.file.vo.preview.WopiFileVo;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import java.io.InputStream;
/**
* @Classname FilePreviceService
* @Description TODO
* @Date 2025/2/26 18:24
* @Created by violet
*/
public interface FilePreviewService {
CheckFileInfoVo createPreviewInfo(String fileId);
void saveFile(String fileId, InputStream content);
void getFileContent(String fileId, HttpServletResponse response);
}

View File

@ -0,0 +1,115 @@
package com.wmyun.module.infra.service.file;
import cn.hutool.core.io.IoUtil;
import com.wmyun.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.wmyun.framework.security.core.LoginUser;
import com.wmyun.framework.security.core.util.SecurityFrameworkUtils;
import com.wmyun.module.infra.controller.admin.file.vo.preview.CheckFileInfoVo;
import com.wmyun.module.infra.dal.dataobject.file.FileDO;
import com.wmyun.module.infra.dal.mysql.file.FileMapper;
import com.wmyun.module.infra.framework.file.core.client.FileClient;
import com.wmyun.module.infra.framework.file.core.utils.FileTypeUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;
import static com.wmyun.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
/**
* @Description: TODO
* @Date: 2025/2/26 18:25
* @Created: by ZZSLL
*/
@Service
public class FilePreviewServiceImpl implements FilePreviewService {
@Autowired
private FileMapper fileMapper;
@Resource
private FileConfigService fileConfigService;
@Override
public CheckFileInfoVo createPreviewInfo(String fileId) {
FileDO file = queryFileInfoByFileId(fileId);
if (file == null) {
return null;
}
LoginUser user = SecurityFrameworkUtils.getLoginUser();
return CheckFileInfoVo.builder()
.baseFileName(file.getName())
.ownerId(file.getUpdater())
.size(Long.valueOf(file.getSize()))
.userId(String.valueOf(Optional.ofNullable(user)
.map(LoginUser::getId)
.orElse(0L)))
.userFriendlyName(Optional.ofNullable(user)
.map(LoginUser::getInfo)
.map(info -> info.get(LoginUser.INFO_KEY_NICKNAME))
.orElse(""))
.version("")
.supportsDeleteFile(false)
.supportsRename(false)
.supportsUpdate(true)
.supportsUserInfo(true)
.readOnly(true)
.userCanRename(false)
.userCanWrite(false)
.downloadUrl(file.getUrl())
.fileUrl(file.getUrl())
.hostViewUrl("")
.build();
}
/**
* 查询文件信息
* @param fileId 文件id
* @return
*/
private FileDO queryFileInfoByFileId(String fileId) {
LambdaQueryWrapperX<FileDO> queryWrapperX = new LambdaQueryWrapperX<>();
queryWrapperX.eq(FileDO::getId, fileId).eq(FileDO::getDeleted, false);
List<FileDO> files = fileMapper.selectList(queryWrapperX);
return CollectionUtils.isNotEmpty(files) ? files.get(0) : null;
}
@Override
public void saveFile(String fileId, InputStream content) {
FileDO existFile = queryFileInfoByFileId(fileId);
FileClient client = fileConfigService.getMasterFileClient();
if (existFile == null) {
throw new RuntimeException("不支持创建文件");
}
byte[] data = IoUtil.readBytes(content);
String type = FileTypeUtils.getMineType(data, existFile.getName());
try {
String url = client.upload(data, existFile.getPath(), type);
existFile.setUrl(url);
fileMapper.updateById(existFile);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void getFileContent(String fileId, HttpServletResponse response) {
FileDO file = queryFileInfoByFileId(fileId);
if (file == null) {
return;
}
FileClient client = fileConfigService.getFileClient(file.getConfigId());
try {
writeAttachment(response, file.getName(), client.getContent(file.getPath()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -127,4 +127,4 @@ spring:
# 芋道配置项,设置当前项目所有自定义的配置
wmyun:
demo: true # 开启演示模式
demo: false # 开启演示模式

View File

@ -166,6 +166,7 @@ wmyun:
enable: true
ignore-urls:
- /admin-api/infra/file/*/get/** # 获取图片,和租户无关
- /admin-api/infra/file/preview/**
ignore-tables:
- infra_codegen_column
- infra_codegen_table

View File

@ -216,4 +216,6 @@ public interface AdminUserService {
*/
boolean isPasswordMatch(String rawPassword, String encodedPassword);
String encodePassword(String password);
}

View File

@ -520,7 +520,8 @@ public class AdminUserServiceImpl implements AdminUserService {
* @param password 密码
* @return 加密后的密码
*/
private String encodePassword(String password) {
@Override
public String encodePassword(String password) {
return passwordEncoder.encode(password);
}