검증게시판 첨부파일 첨부 및 삭제기능

This commit is contained in:
Macbook
2026-06-13 01:55:19 +09:00
parent 3a4cc1ff3e
commit 67c70bff05
4 changed files with 85 additions and 17 deletions
@@ -93,7 +93,7 @@ public class VerificationIssueImageService {
String contentType = resolveContentType(file, data);
validateFile(data.length, contentType);
String original = sanitizeFileName(file.getOriginalFilename());
String original = resolveOriginalFileName(file, contentType);
String storedName = UUID.randomUUID() + "_" + original;
Path target = issueDir.resolve(storedName);
Files.write(target, data);
@@ -140,6 +140,12 @@ public class VerificationIssueImageService {
.map(GcVerificationIssueImage::getContentType);
}
public Optional<String> fileName(Long issueId, Long imageId) {
return imageRepository.findById(imageId)
.filter(img -> issueId.equals(img.getIssueId()))
.map(GcVerificationIssueImage::getFileName);
}
@Transactional
public boolean delete(Long issueId, Long imageId) throws IOException {
Optional<GcVerificationIssueImage> opt = imageRepository.findById(imageId)
@@ -234,9 +240,7 @@ public class VerificationIssueImageService {
if (header != null && header.length >= 8
&& header[0] == (byte) 0xD0 && header[1] == (byte) 0xCF
&& header[2] == 0x11 && header[3] == (byte) 0xE0) {
if (fileName != null && fileName.toLowerCase().endsWith(".hwp")) {
return "application/x-hwp";
}
return "application/x-hwp";
}
return null;
}
@@ -274,10 +278,47 @@ public class VerificationIssueImageService {
}
}
private String sanitizeFileName(String name) {
if (name == null || name.isBlank()) return "attachment.bin";
String base = Paths.get(name).getFileName().toString();
return base.replaceAll("[^a-zA-Z0-9._\\-가-힣]", "_");
/** 업로드 multipart의 원본 파일명·확장자를 그대로 보존 (경로·위험 문자만 제거) */
private String resolveOriginalFileName(MultipartFile file, String contentType) {
String name = extractBaseFileName(file.getOriginalFilename());
if (name == null || name.isBlank()) {
name = defaultNameForContentType(contentType);
}
return truncateFileName(name, 255);
}
private static String extractBaseFileName(String name) {
if (name == null || name.isBlank()) return null;
String normalized = name.replace('\\', '/').trim();
int slash = normalized.lastIndexOf('/');
String base = slash >= 0 ? normalized.substring(slash + 1) : normalized;
if (base.isBlank() || ".".equals(base) || "..".equals(base)) return null;
base = base.replaceAll("[\\x00-\\x1F<>:\"|?*]", "");
return base.isBlank() ? null : base;
}
private static String defaultNameForContentType(String contentType) {
if (contentType == null) return "attachment";
return switch (contentType) {
case "application/x-hwp", "application/haansoft-hwp",
"application/vnd.hancom.hwp", "application/hwp+zip" -> "attachment.hwp";
case "application/vnd.hancom.hwpx", "application/x-hwpx" -> "attachment.hwpx";
case "application/zip", "application/x-zip-compressed" -> "attachment.zip";
default -> "attachment";
};
}
private static String truncateFileName(String name, int maxLen) {
if (name.length() <= maxLen) return name;
int dot = name.lastIndexOf('.');
if (dot > 0 && dot < name.length() - 1) {
String ext = name.substring(dot);
int baseMax = maxLen - ext.length();
if (baseMax > 0) {
return name.substring(0, baseMax) + ext;
}
}
return name.substring(0, maxLen);
}
private VerificationIssueImageDto toDto(GcVerificationIssueImage e) {