fix: store uploads under relative file directory

main
chenyuan 4 weeks ago
parent c956d06d49
commit 4c15249264

@ -87,7 +87,8 @@ public class WebConfigurerAdapter implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/file/**").addResourceLocations("file:" + normalizeFileLocation(filePath)).setCachePeriod(0);
String storageRoot = LocalFileUtil.resolveStorageRoot(filePath).toString();
registry.addResourceHandler("/file/**").addResourceLocations("file:" + normalizeFileLocation(storageRoot)).setCachePeriod(0);
registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0);
}

@ -1,6 +1,7 @@
package com.sztzjy.linkCommerce.controller.common;
import com.sztzjy.linkCommerce.util.file.IFileUtil;
import com.sztzjy.linkCommerce.util.file.LocalFileUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -41,11 +42,6 @@ public class CommonUploadController {
}
private String publicFileUrl(String filePath) {
String baseUrl = fileUrl == null ? "" : fileUrl.trim();
while (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
}
String relativePath = filePath == null ? "" : filePath.trim();
return baseUrl + (relativePath.startsWith("/") ? relativePath : "/" + relativePath);
return LocalFileUtil.publicFileUrl(fileUrl, filePath);
}
}

@ -11,6 +11,7 @@ import com.sztzjy.linkCommerce.util.CompareListsUtil;
import com.sztzjy.linkCommerce.util.FileHashUtil;
import com.sztzjy.linkCommerce.util.ResultEntity;
import com.sztzjy.linkCommerce.util.file.IFileUtil;
import com.sztzjy.linkCommerce.util.file.LocalFileUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@ -401,14 +402,14 @@ public class ProductPlanningController {
uploadExperment.setModule(module);
uploadExperment.setUserId(userId);
stuUploadExpermentMapper.insertSelective(uploadExperment);
return new ResultEntity<>(HttpStatus.OK, "上传成功!", url + filePath);
return new ResultEntity<>(HttpStatus.OK, "上传成功!", LocalFileUtil.publicFileUrl(url, filePath));
} else {
StuUploadExperment uploadExperment = stuUploadExpermentList.get(0);
uploadExperment.setUpdateTime(new Date());
uploadExperment.setFilePath(filePath);
stuUploadExpermentMapper.updateByPrimaryKeySelective(uploadExperment);
return new ResultEntity<>(HttpStatus.OK, "上传成功!", url + filePath);
return new ResultEntity<>(HttpStatus.OK, "上传成功!", LocalFileUtil.publicFileUrl(url, filePath));
}

@ -10,6 +10,8 @@ import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
@ -18,11 +20,17 @@ import java.util.List;
public class LocalFileUtil implements IFileUtil{
private final static List<String> excludeSp = Arrays.asList("exe", "bin", "sh");
private static final String FILE_PREFIX = "file/";
private final String localPath;
public LocalFileUtil(String localPath) {
this.localPath = localPath;
this.localPath = resolveStorageRoot(localPath).toString();
try {
Files.createDirectories(Paths.get(this.localPath));
} catch (IOException exception) {
throw new IllegalArgumentException("创建文件存储目录失败", exception);
}
}
@Override
@ -109,10 +117,28 @@ public class LocalFileUtil implements IFileUtil{
@Override
public String getFullPath(String relativePath) {
if (!relativePath.startsWith("/")) {
relativePath = "/" + relativePath;
Path root = Paths.get(localPath);
Path target = root.resolve(normalizeStorageRelativePath(relativePath)).normalize();
Assert.isTrue(target.startsWith(root), "文件路径错误");
return target.toString();
}
public static Path resolveStorageRoot(String configuredPath) {
Assert.hasText(configuredPath, "文件存储路径不能为空");
Path configured = Paths.get(configuredPath.trim());
if (!configured.isAbsolute()) {
configured = Paths.get(System.getProperty("user.dir")).resolve(configured);
}
return localPath + relativePath;
return configured.toAbsolutePath().normalize();
}
public static String publicFileUrl(String fileUrl, String storedPath) {
Assert.hasText(fileUrl, "文件访问地址不能为空");
String baseUrl = fileUrl.trim();
while (baseUrl.endsWith("/")) {
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
}
return baseUrl + "/" + normalizeStorageRelativePath(storedPath);
}
@Override
@ -137,8 +163,8 @@ public class LocalFileUtil implements IFileUtil{
String year = Integer.toString(c.get(Calendar.YEAR));
String month = Integer.toString(c.get(Calendar.MONTH) + 1);
String day = Integer.toString(c.get(Calendar.DATE));
StringBuilder path = new StringBuilder();
path.append("/").append(year)
StringBuilder path = new StringBuilder(FILE_PREFIX);
path.append(year)
.append("/").append(month)
.append("/").append(day)
.append("/").append(fileName);
@ -173,4 +199,17 @@ public class LocalFileUtil implements IFileUtil{
throw new IllegalArgumentException("上传文件失败,IO错误");
}
}
private static String normalizeStorageRelativePath(String relativePath) {
Assert.hasText(relativePath, "文件路径不能为空");
String normalized = relativePath.trim().replace("\\", "/");
while (normalized.startsWith("/")) {
normalized = normalized.substring(1);
}
if (normalized.startsWith(FILE_PREFIX)) {
normalized = normalized.substring(FILE_PREFIX.length());
}
Assert.hasText(normalized, "文件路径不能为空");
return normalized;
}
}

@ -20,7 +20,7 @@ spring:
# 文件存储
file:
type: local
path: E:/workspace/dianshang/link_commerce/uploadFile
path: file
url: ${FILE_URL:http://localhost:7548/file}
#path: D:/tianzeProject/linkCommerce/
#url: "http://192.168.2.8:147/file"

@ -19,7 +19,7 @@ class CommonUploadControllerTest {
@Test
void uploadReturnsAbsoluteConfiguredFileUrl() {
IFileUtil fileUtil = mock(IFileUtil.class);
when(fileUtil.upload(any(MultipartFile.class))).thenReturn("/2026/8/1/example.docx");
when(fileUtil.upload(any(MultipartFile.class))).thenReturn("file/2026/8/1/example.docx");
CommonUploadController controller = new CommonUploadController();
ReflectionTestUtils.setField(controller, "fileUtil", fileUtil);
ReflectionTestUtils.setField(controller, "fileUrl", "https://files.example.com/file/");

@ -0,0 +1,44 @@
package com.sztzjy.linkCommerce.util.file;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class LocalFileUtilTest {
@Test
void createsRelativeFileRootAndUsesFilePrefixedDatePaths() throws IOException {
Path relativeRoot = Paths.get("target", "upload-root-" + UUID.randomUUID());
try {
LocalFileUtil fileUtil = new LocalFileUtil(relativeRoot.toString());
assertTrue(Files.isDirectory(relativeRoot));
String path = fileUtil.getDiskRelativePath("example", "pdf");
assertTrue(path.startsWith("file/"));
assertTrue(path.endsWith("/example.pdf"));
assertEquals(relativeRoot.toAbsolutePath().normalize().resolve(path.substring("file/".length())).toString(),
fileUtil.getFullPath(path));
assertEquals(relativeRoot.toAbsolutePath().normalize().resolve("2024/1/2/legacy.pdf").toString(),
fileUtil.getFullPath("/2024/1/2/legacy.pdf"));
} finally {
if (Files.exists(relativeRoot)) {
try (java.util.stream.Stream<Path> files = Files.walk(relativeRoot)) {
files.sorted(java.util.Comparator.reverseOrder()).forEach(path -> {
try {
Files.deleteIfExists(path);
} catch (IOException exception) {
throw new IllegalStateException(exception);
}
});
}
}
}
}
}
Loading…
Cancel
Save