CustomFileUtils 살펴보기
CustomFileUtil은 파일 저장 처리 기능을 구현하는 클래스입니다. 종속성을 줄이고 재사용성을 높이기 위해 매개변수는 String 및 MultipartFile과 같은 유형만 수신하도록 설계되었습니다.
– 사용자 정의 util 클래스로 모든 메소드는 정적으로 선언됩니다.
– PropertyUtil의 get 메소드를 사용하여 application.properties에 정의된 환경 값에 액세스합니다.
– 이미지를 확인할 수 없는 경우 “images/empty/noimg.jpeg” 파일로 대체됩니다.
– 메소드 getAccessUrl, getAccessUrls, findFiles, findFile, saveFiles, saveFile, deleteDirectory 및 deleteAllFiles가 구현됩니다.
– 별도로 설정한 리소스 경로를 받아 리소스 URL 경로 또는 로컬 접근 경로를 자동으로 추가합니다.
@Slf4j
public class CustomFileUtils {
private static final String EMPTY_PATH = "empty/noimg.jpeg";
public static String getAccessUrl(String filePath) {
return getAccessImgUrl() + (findFile(filePath) == null ? EMPTY_PATH : filePath);
}
public static List<String> getAccessUrls(String directoryPath) {
return findFiles(directoryPath).stream()
.map(f -> getAccessImgUrl() + (f == null ? EMPTY_PATH : directoryPath + f))
.collect(Collectors.toList());
}
public static String findFile(String filePath) {
try {
File file = new File(getImgFolderPath() + filePath);
if (file.isFile())
return file.getName();
} catch (NullPointerException ex) {
log.error("(파일 경로가 유효하지 않습니다.) : {}", "요청 경로 : " + filePath);
}
return null;
}
public static List<String> findFiles(String directoryPath) {
List<String> found = new ArrayList<>();
try {
File() files = new File(getImgFolderPath() + directoryPath).listFiles();
for (File f : files) {
if (f.isFile()) {
found.add(f.getName());
}
}
} catch (NullPointerException ex) {
log.error("(디렉토리 경로가 유효하지 않습니다.) : {}", "요청 경로 : " + directoryPath);
found.add(null);
}
return found;
}
public static List<String> saveFiles(List<MultipartFile> files, String directoryPath) {
List<String> uploadNames = new ArrayList<>();
for (MultipartFile f : files) {
uploadNames.add(saveFile(f, directoryPath));
}
return uploadNames;
}
public static String saveFile(MultipartFile file, String directoryPath) {
String extension = file.getContentType().replaceFirst(".*/", ".");
String uploadName = UUID.randomUUID() + extension;
String path = getImgFolderPath() + directoryPath;
try {
Files.createDirectories(Paths.get(path));
file.transferTo(new File(path, uploadName));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return uploadName;
}
public static void deleteDirectory(String directoryPath) {
deleteAllFiles(getImgFolderPath() + directoryPath);
}
private static void deleteAllFiles(String directoryPath) {
File dir = new File(directoryPath);
File() files = dir.listFiles();
try {
for (File f : files) {
String target = directoryPath + f.getName();
log.info(target);
if (f.isDirectory())
deleteAllFiles(target + "/");
Files.deleteIfExists(Paths.get(target));
log.info("(파일을 삭제했습니다.) : {}", "삭제 파일 : " + target);
}
Files.deleteIfExists(Paths.get(directoryPath));
} catch (NullPointerException ex1) {
log.error("(디렉토리 경로가 유효하지 않습니다.) : {}", "요청 경로 : " + directoryPath);
} catch (IOException ex2) {
throw new RuntimeException(ex2);
}
}
}
관습FileUtils 구문 분석
1. 참고사항
— @Slf4j
기능 : 로그 관련 기능을 편리하게 사용할 수 있습니다.
분석하다 : 현재 예외 관련 로그를 처리하는 클래스는 구현되어 있지 않습니다. 이후에 로그 처리를 분리하는 경우 해당 주석을 제거해야 합니다.
두 번째 필드
– 개인 최종 문자열 EMPTY_PATH = “빈/noimg.jpeg”
기능 : 이미지 확인이 불가능할 때 대체 이미지로 사용됩니다.
분석하다 : 로컬에 저장된 이미지를 찾을 수 없거나 손상되어 열 수 없는 경우 논리 자체를 중지하는 것보다 문제가 발생했음을 시각적으로 나타내는 대체 이미지를 사용하는 것이 적절하다고 생각했습니다. 변환기 클래스인 경우
세 번째 방법
– 공개 정적 문자열 getAccessUrl(문자열 파일 경로)
기능 : 파라미터로 받은 파일 경로를 이용하여 파일에 접근할 수 있는 URL을 빌드하여 반환합니다.
분석하다 :
- findFile 메서드를 호출하여 파일의 유효성을 검사합니다. 유효성에 따라 리소스 액세스 URL이 대체 파일 경로 또는 매개 변수로 수신 및 반환되는 파일 경로에 추가됩니다.
- getAccessImgUrl 메서드를 사용하여 PropertyUtil에 정의된 리소스 액세스 URL의 값을 가져옵니다.
- 중복을 제거하고 getAccessImgUrl 호출에 대한 코드 가독성을 향상시키기 위해 개인 변환기 방법을 구현할 수 있다고 생각합니다.
- 함수의 특성상 CustomFileUtils 클래스에 구현하는 것보다 함수를 분리하여 다른 클래스에 구현하는 것이 적절하다.
– 공개 정적 목록
기능 : 파라미터로 받은 디렉토리 아래의 모든 파일에 접근할 수 있는 URL을 빌드하여 반환합니다.
분석하다 :
- 디렉토리의 모든 파일을 찾으려면 findFiles 메소드를 호출하십시오.
- getAccessImgUrl 메서드를 사용하여 PropertyUtil에 정의된 리소스 액세스 URL의 값을 가져옵니다.
- 개인 변환기 메서드를 구현하면 getAccessImgUrl 호출에 대한 중복을 제거하고 코드 가독성을 향상시킬 수 있습니다.
- 함수의 특성상 CustomFileUtils 클래스에 구현하는 것보다 함수를 분리하여 다른 클래스에 구현하는 것이 적절하다.
– 공개 정적 문자열 findFile(문자열 파일 경로)
기능 : 매개변수로 받은 파일경로에 있는 파일을 검증하고 결과에 따라 파일명 또는 null을 반환한다.
분석하다 :
- 리소스 액세스 경로를 자동으로 마운트하여 서비스 계층의 노출을 제거할 수 있습니다.
- 파일 유효성 검사 처리가 잘못되었습니다.
- 중복을 제거하고 getImgFolderPath 호출에 대한 코드 가독성을 개선하기 위해 전용 변환기 메서드를 구현할 수 있습니다.
– 공개 정적 목록
기능 : 매개변수화된 디렉터리의 모든 파일은 유효성이 검사되고 결과에 따라 파일 이름 목록 또는 null로 반환됩니다.
분석하다 :
- 리소스 액세스 경로를 자동으로 마운트하여 서비스 계층의 노출을 제거할 수 있습니다.
- 하위 디렉토리가 존재하는지 여부를 판별하는 논리를 추가해야 합니다.
- 파일 유효성 검사 처리가 잘못되었습니다.
- 중복을 제거하고 getImgFolderPath 호출에 대한 코드 가독성을 개선하기 위해 전용 변환기 메서드를 구현할 수 있습니다.
– 공개 정적 목록
기능 : 매개변수로 받은 모든 파일은 지정된 디렉토리에 저장됩니다.
분석하다 : saveFile 메소드로 모든 파일을 저장하고 저장된 파일명을 리스트로 반환합니다.
– 공개 정적 문자열 saveFile(MultipartFile 파일, String directoryPath)
기능 : 파라미터로 받은 파일을 지정된 디렉토리에 저장합니다.
분석하다 :
- 파일에서 확장자를 추출하고 UUID를 사용하여 임의의 파일 이름을 생성합니다.
- 파일 및 디렉터리에 대한 유효성 검사 논리를 추가해야 합니다.
- 중복을 제거하고 getImgFolderPath 호출에 대한 코드 가독성을 개선하기 위해 전용 변환기 메서드를 구현할 수 있습니다.
– 공개 정적 무효 deleteDirectory(String directoryPath)
기능 : 리소스 접근 경로를 디렉터리 경로에 추가합니다.
분석하다 : 리소스 액세스 경로를 추가한 다음 deleteAllFiles 메서드를 호출하기만 하면 됩니다. 중복을 제거하고 getImgFolderPath 호출에 대한 코드 가독성을 개선하기 위해 전용 변환기 메서드를 구현할 수 있습니다.
– private static void deleteAllFiles(문자열 디렉토리 경로)
기능 : 디렉토리 아래의 모든 파일과 디렉토리를 제거한 다음 해당 디렉토리도 제거합니다.
분석하다 : 파일 및 디렉터리에 대한 유효성 검사 논리를 추가해야 합니다.
관습FileUtils 리팩토링
변경된 경우 ■ 추가된 경우 색상 ■ 삭제된 경우 색상 ■ 색상으로 구분됩니다. 수정된 코드는 다음과 같습니다.
/**
* 해당 클래스는 파일에 대한 저장, 조회, 제거 기능을 처리하기 위해 구현되었다.
*/
public class CustomFileUtils {
/**
* 유효 파일 탐색 과정에서 발생할 수 있는 비효율적인 저장 방식의 개선과 스프링의 멀티 쓰레드 환경을 고려한
* 필드이다.
*/
private static final ThreadLocal<List<String>> foundImage = new ThreadLocal<>();
/**
* recursivelyFind 메서드를 호출하기 전과 후에 ThreadLocal에 대해 처리를 수행하는 메서드이다.
* @param path : 파일을 탐색할 경로
* @return : 탐색 과정에서 확인한 모든 파일들의 전체 경로
*/
public static List<String> findFilePaths(String path) {
foundImage.set(new ArrayList<>());
recursivelyFind(path);
List<String> found = new ArrayList<>(foundImage.get());
foundImage.remove();
return found;
}
/**
* 해당 경로 하위에 존재하는 모든 파일들을 재귀적으로 탐색 및 찾은 파일의 전체 경로를 저장한다.
* @param path : 파일을 탐색할 경로
*/
private static void recursivelyFind(String path) {
File() files = new File(path).listFiles();
if (files == null) {
return;
}
for (File f : files) {
String target = path + "/" + f.getName();
if (f.isDirectory()) {
recursivelyFind(target);
}
foundImage.get().add(target);
}
}
/**
* 요청으로 받은 MultipartFile 타입의 파일 각각을 saveFile 메서드의 매개변수로 전달한다.
* @param files : 클라이언트 요청으로 받은 MultipartFile 타입의 파일들
* @param path : 파일을 저장할 경로
* @return 저장된 파일들의 파일명 반환
*/
public static List<String> saveFiles(List<MultipartFile> files, String path) {
List<String> uploadNames = new ArrayList<>();
for (MultipartFile f : files) {
uploadNames.add(saveFile(f, path));
}
return uploadNames;
}
/**
* MultipartFile에서 추출한 확장자와 UUID로 생성한 문자열을 이용해 파일명을 정의하고, 매개변수로 입력받은
* 경로에 파일을 저장한다.
* @param file : 클라이언트 요청으로 받은 MultipartFile 타입의 파일
* @param path : 파일을 저장할 경로
* @return 저장된 파일의 파일명 반환
*/
public static String saveFile(MultipartFile file, String path) {
if (file.isEmpty() || file.getContentType() == null) {
throw new IllegalArgumentException();
}
String extension = file.getContentType().replaceFirst(".*/", ".");
String uploadName = UUID.randomUUID() + extension;
try {
Files.createDirectories(Paths.get(path));
file.transferTo(new File(path, uploadName));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return uploadName;
}
/**
* 디렉토리 내에 존재하는 모든 파일을 재귀적으로 탐색 및 삭제 후 해당 디렉토리도 삭제한다.
* @param path : 삭제하려는 파일 경로
*/
public static void deleteDirectory(String path) {
File() files = new File(path).listFiles();
if (files == null) {
return;
}
try {
for (File f : files) {
String target = path + "/" + f.getName();
if (f.isDirectory()) {
deleteDirectory(target);
}
Files.deleteIfExists(Paths.get(target));
}
Files.deleteIfExists(Paths.get(path));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
1. 참고사항
– @Slf4j
로그 처리 기준이 마련되면 다시 시행할 예정이다.
두 번째 필드
– 개인 최종 문자열 EMPTY_PATH = “빈/noimg.jpeg”
PathConverter 클래스의 새로운 구현으로 AccessUrl과 관련된 메서드가 제거되어 필드가 더 이상 사용되지 않습니다.
– 비공개 상태 최종 ThreadLocal
ThreadLocal 방식은 Spring이 스레드 풀을 사용하는 요청 처리 방식을 사용한다는 사실과 재귀적으로 파일 경로를 찾는 효율성을 고려하여 선택되었다.
세 번째 방법
– 공개 정적 문자열 getAccessUrl(문자열 파일 경로)
해당 절차를 파일 관련 처리가 아닌 변환기 기능으로 분리하는 것이 적절하다고 판단되었습니다.
– 공개 정적 목록
해당 절차를 파일 관련 처리가 아닌 변환기 기능으로 분리하는 것이 적절하다고 판단되었습니다.
– 공개 정적 문자열 findFile(문자열 파일 경로)
파일명을 포함한 경로는 현재 DB에 저장되어 있지 않아 불필요한 것으로 판단되어 삭제되었습니다.
– 공개 정적 목록
ThreadLocal을 사용했기 때문에 재귀적 Find 메서드 호출과 LocalThread 핸들러를 수행하도록 해당 메서드를 수정했다.
– private static void recursivelyFind(문자열 경로)
기존 findFiles 함수에서 예외 처리 방식과 대상 경로 내의 모든 파일을 재귀적으로 찾는 방식이 변경되었습니다.
– 공개 정적 문자열 saveFile(MultipartFile 파일, 문자열 경로)
MultipartFile의 경우 컨트롤러 수준에서 유효성 검사를 수행할 수 없습니다. 따라서 저장 시 파일에 유효성 검사 논리가 추가되었습니다.
– 공공 정적 무효 deleteDirectory(문자열 디렉토리 경로)
대상 경로 아래의 모든 파일을 재귀적으로 제거합니다.
– 개인 정적 무효 deleteAllFiles(String directoryPath)
PathConverter 클래스가 구현되었기 때문에 기본 리소스 접근 경로를 추가하는 기능은 더 이상 필요하지 않으므로 메서드에서 처리하는 기능은 deleteDirectroy에서 처리합니다.