package com.vincent.rsf.server.system.controller;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.vincent.rsf.framework.common.R;
|
import com.vincent.rsf.framework.exception.CoolException;
|
import com.vincent.rsf.server.common.utils.FileServerUtil;
|
import com.vincent.rsf.server.system.entity.Config;
|
import com.vincent.rsf.server.system.enums.StatusType;
|
import com.vincent.rsf.server.system.service.ConfigService;
|
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletResponse;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.util.StringUtils;
|
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.io.IOException;
|
import java.io.OutputStream;
|
import java.nio.file.Path;
|
import java.nio.file.Paths;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Locale;
|
import java.util.Map;
|
import java.util.Set;
|
|
@RestController
|
public class ProjectLogoController extends BaseController {
|
|
private static final String PROJECT_LOGO_FLAG = "PROJECT_LOGO";
|
private static final String PROJECT_COPYRIGHT_FLAG = "PROJECT_COPYRIGHT";
|
private static final Set<String> ALLOWED_IMAGE_TYPES = Set.of(
|
"image/png",
|
"image/jpeg",
|
"image/jpg",
|
"image/gif",
|
"image/bmp",
|
"image/webp",
|
"image/svg+xml",
|
"image/x-icon",
|
"image/vnd.microsoft.icon"
|
);
|
private static final Set<String> ALLOWED_IMAGE_EXTENSIONS = Set.of(
|
".png",
|
".jpg",
|
".jpeg",
|
".gif",
|
".bmp",
|
".webp",
|
".svg",
|
".ico"
|
);
|
private static final Path PROJECT_LOGO_ROOT = Paths.get(
|
System.getProperty("user.home"),
|
".rsf",
|
"uploads",
|
"logo"
|
).toAbsolutePath().normalize();
|
|
@Autowired
|
private ConfigService configService;
|
|
@GetMapping("/config/public/project-logo")
|
public R getProjectLogoConfig() {
|
return getEnabledConfigByFlag(PROJECT_LOGO_FLAG);
|
}
|
|
@GetMapping("/config/public/project-copyright")
|
public R getProjectCopyrightConfig() {
|
return getEnabledConfigByFlag(PROJECT_COPYRIGHT_FLAG);
|
}
|
|
private R getEnabledConfigByFlag(String flag) {
|
List<Config> configs = configService.list(new LambdaQueryWrapper<Config>()
|
.eq(Config::getFlag, flag)
|
.eq(Config::getStatus, StatusType.ENABLE.val)
|
.last("limit 1"));
|
return R.ok().add(configs.stream().findFirst().orElse(null));
|
}
|
|
@PreAuthorize("hasAnyAuthority('system:config:save','system:config:update')")
|
@PostMapping("/config/logo/upload")
|
public R uploadProjectLogo(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws IOException {
|
validateImageFile(file);
|
File savedFile = FileServerUtil.upload(file, PROJECT_LOGO_ROOT.toString(), true);
|
String relativePath = PROJECT_LOGO_ROOT.relativize(savedFile.toPath().toAbsolutePath().normalize())
|
.toString()
|
.replace(File.separatorChar, '/');
|
String url = request.getContextPath() + "/file/logo?path=" + relativePath;
|
|
Map<String, Object> payload = new HashMap<>();
|
payload.put("name", savedFile.getName());
|
payload.put("path", relativePath);
|
payload.put("url", url);
|
return R.ok().add(payload);
|
}
|
|
@GetMapping("/file/logo")
|
public void previewProjectLogo(@RequestParam("path") String path, HttpServletRequest request, HttpServletResponse response) throws IOException {
|
File file = resolveLogoPath(path).toFile();
|
if (!file.exists() || !file.isFile()) {
|
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
return;
|
}
|
|
response.setContentType(resolveImageContentType(file.getName()));
|
response.setHeader("Cache-Control", "public, max-age=86400");
|
response.setContentLengthLong(file.length());
|
|
try (FileInputStream inputStream = new FileInputStream(file);
|
OutputStream outputStream = response.getOutputStream()) {
|
inputStream.transferTo(outputStream);
|
outputStream.flush();
|
}
|
}
|
|
private void validateImageFile(MultipartFile file) {
|
if (file == null || file.isEmpty()) {
|
throw new CoolException("上传文件不能为空");
|
}
|
if (!isAllowedImageType(file.getContentType()) && !isAllowedImageExtension(file.getOriginalFilename())) {
|
throw new CoolException("只支持上传图片文件");
|
}
|
}
|
|
private boolean isAllowedImageType(String contentType) {
|
if (!StringUtils.hasText(contentType)) {
|
return false;
|
}
|
return ALLOWED_IMAGE_TYPES.contains(contentType.toLowerCase(Locale.ROOT));
|
}
|
|
private boolean isAllowedImageExtension(String fileName) {
|
if (!StringUtils.hasText(fileName) || !fileName.contains(".")) {
|
return false;
|
}
|
String extension = fileName.substring(fileName.lastIndexOf(".")).toLowerCase(Locale.ROOT);
|
return ALLOWED_IMAGE_EXTENSIONS.contains(extension);
|
}
|
|
private Path resolveLogoPath(String relativePath) {
|
if (!StringUtils.hasText(relativePath)) {
|
throw new CoolException("文件路径不能为空");
|
}
|
Path resolvedPath = PROJECT_LOGO_ROOT.resolve(relativePath).normalize();
|
if (!resolvedPath.startsWith(PROJECT_LOGO_ROOT)) {
|
throw new CoolException("非法文件路径");
|
}
|
return resolvedPath;
|
}
|
|
private String resolveImageContentType(String fileName) {
|
String normalizedName = StringUtils.hasText(fileName) ? fileName.toLowerCase(Locale.ROOT) : "";
|
if (normalizedName.endsWith(".png")) {
|
return "image/png";
|
}
|
if (normalizedName.endsWith(".jpg") || normalizedName.endsWith(".jpeg")) {
|
return "image/jpeg";
|
}
|
if (normalizedName.endsWith(".gif")) {
|
return "image/gif";
|
}
|
if (normalizedName.endsWith(".bmp")) {
|
return "image/bmp";
|
}
|
if (normalizedName.endsWith(".webp")) {
|
return "image/webp";
|
}
|
if (normalizedName.endsWith(".svg")) {
|
return "image/svg+xml";
|
}
|
if (normalizedName.endsWith(".ico")) {
|
return "image/x-icon";
|
}
|
return "application/octet-stream";
|
}
|
}
|