add extensions,true path for file
Test Workflow / test (push) Successful in 2s

This commit is contained in:
vladp
2025-12-16 14:27:03 +07:00
parent b3f29935f6
commit 4a6de6ad9a
5 changed files with 30 additions and 24 deletions
@@ -47,7 +47,7 @@ public class FileUploadController {
try { try {
FileUploadSession session = fileUploadService.initUpload(authToken.getUser().getId(), request.getFileName(), FileUploadSession session = fileUploadService.initUpload(authToken.getUser().getId(), request.getFileName(),
request.getFileType(), request.getFileSize()); request.getFileType(), request.getExtension(), request.getFileSize());
return ResponseEntity.ok(FileApiResponse.success(session)); return ResponseEntity.ok(FileApiResponse.success(session));
} catch (Exception e) { } catch (Exception e) {
@@ -14,6 +14,9 @@ public class InitUploadRequest {
@NotBlank(message = "File type is required") @NotBlank(message = "File type is required")
private String fileType; private String fileType;
@NotBlank(message = "File extension is required")
private String extension;
@NotNull(message = "File size is required") @NotNull(message = "File size is required")
@Positive(message = "File size must be positive") @Positive(message = "File size must be positive")
private Long fileSize; private Long fileSize;
@@ -62,6 +62,9 @@ public class FileUploadSession {
@Column(name = "last_error") @Column(name = "last_error")
private String lastError; private String lastError;
@Column(name = "extension")
private String extension;
@Column(name = "retry_count") @Column(name = "retry_count")
private Integer retryCount = 0; private Integer retryCount = 0;
@@ -8,7 +8,7 @@ import java.io.IOException;
public interface FileUploadService { public interface FileUploadService {
FileUploadSession initUpload(Long userId, String fileName, FileUploadSession initUpload(Long userId, String fileName,
String fileType, long fileSize); String fileType, String extension, long fileSize);
UploadProgressResponse uploadChunk(String uploadId, Integer chunkNumber, UploadProgressResponse uploadChunk(String uploadId, Integer chunkNumber,
MultipartFile chunkFile) throws IOException; MultipartFile chunkFile) throws IOException;
@@ -81,7 +81,7 @@ public class FileUploadServiceImpl implements FileUploadService {
@Override @Override
@Transactional @Transactional
public FileUploadSession initUpload(Long userId, String fileName, public FileUploadSession initUpload(Long userId, String fileName,
String fileType, long fileSize) { String fileType, String extension, long fileSize) {
log.info("Initializing upload for user {}: {} ({} bytes, type: {})", log.info("Initializing upload for user {}: {} ({} bytes, type: {})",
userId, fileName, fileSize, fileType); userId, fileName, fileSize, fileType);
@@ -101,6 +101,7 @@ public class FileUploadServiceImpl implements FileUploadService {
FileUploadSession session = FileUploadSession.builder() FileUploadSession session = FileUploadSession.builder()
.userId(userId) .userId(userId)
.extension(extension)
.fileName(fileName) .fileName(fileName)
.fileType(fileType) .fileType(fileType)
.fileSize(fileSize) .fileSize(fileSize)
@@ -364,7 +365,7 @@ public class FileUploadServiceImpl implements FileUploadService {
Files.createDirectories(userUploadsDir); Files.createDirectories(userUploadsDir);
String safeFileName = generateUniqueFileName(session.getFileName()); String safeFileName = generateUniqueFileName(session.getFileName(), session.getExtension());
Path finalPath = userUploadsDir.resolve(safeFileName); Path finalPath = userUploadsDir.resolve(safeFileName);
if (Files.exists(finalPath)) { if (Files.exists(finalPath)) {
@@ -374,20 +375,24 @@ public class FileUploadServiceImpl implements FileUploadService {
return finalPath; return finalPath;
} }
private String generateUniqueFileName(String originalName) { private String generateUniqueFileName(String originalName, String extension) {
String safeName = originalName.replaceAll("[^a-zA-Z0-9\\.\\-_]", "_"); int dotIndex = originalName.lastIndexOf('.');
log.info("dotIndex: {}", dotIndex);
log.info("originalName: {}", originalName);
String nameWithoutExtension;
if (dotIndex > 0) {
nameWithoutExtension = originalName.substring(0, dotIndex)
.replaceAll("[^a-zA-Z0-9\\-_]", "_");
} else {
nameWithoutExtension = originalName.replaceAll("[^a-zA-Z0-9\\-_]", "_");
}
String timestamp = String.valueOf(System.currentTimeMillis()); String timestamp = String.valueOf(System.currentTimeMillis());
String uuid = UUID.randomUUID().toString().substring(0, 8); String uuid = UUID.randomUUID().toString().substring(0, 8);
int dotIndex = safeName.lastIndexOf('.'); return String.format("%s_%s_%s.%s",
if (dotIndex > 0) { nameWithoutExtension, timestamp, uuid, extension);
String name = safeName.substring(0, dotIndex);
String extension = safeName.substring(dotIndex);
return String.format("%s_%s_%s%s", name, timestamp, uuid, extension);
} else {
return String.format("%s_%s_%s", safeName, timestamp, uuid);
}
} }
private void validateAllChunksExist(FileUploadSession session) throws IOException { private void validateAllChunksExist(FileUploadSession session) throws IOException {
@@ -409,7 +414,7 @@ public class FileUploadServiceImpl implements FileUploadService {
throws IOException { throws IOException {
Path chunkDir = getChunkDirectory(session.getUserId(), session.getUploadId()); Path chunkDir = getChunkDirectory(session.getUserId(), session.getUploadId());
byte[] buffer = new byte[8192]; // 8KB буфер byte[] buffer = new byte[8192];
try (BufferedOutputStream outputStream = new BufferedOutputStream( try (BufferedOutputStream outputStream = new BufferedOutputStream(
Files.newOutputStream(finalPath, StandardOpenOption.CREATE_NEW))) { Files.newOutputStream(finalPath, StandardOpenOption.CREATE_NEW))) {
@@ -596,18 +601,13 @@ public class FileUploadServiceImpl implements FileUploadService {
} }
String lowerType = fileType.toLowerCase(); String lowerType = fileType.toLowerCase();
if (lowerType.startsWith("image/")) { if (lowerType.startsWith("image")) {
return "images"; return "images";
} else if (lowerType.startsWith("video/")) { } else if (lowerType.startsWith("video")) {
return "videos"; return "videos";
} else if (lowerType.startsWith("audio/")) { } else if (lowerType.startsWith("audio")) {
return "audio"; return "audio";
} else if (lowerType.contains("pdf") || } else if (lowerType.contains("document")) {
lowerType.contains("document") ||
lowerType.contains("text") ||
lowerType.contains("msword") ||
lowerType.contains("wordprocessing") ||
lowerType.contains("spreadsheet")) {
return "documents"; return "documents";
} else { } else {
return "other"; return "other";