안녕하세요!
그 동안 제가 취미(??) 생활로 이것저것 수집을 좋아해서 수집활동을 꾸준히 하고있는데
웹상에서 떠돌고있는 폴더들을 크롤링 또는 다운로드를 특정 폴더에 받게끔하면
폴더안에 또 폴더로 파일들이 생성되더라구요 토렌트라던가... 그래서 매번 날잡고
안에있는 파일들을 상위 폴더로 옮기는 작업을 하는데 은근 귀찮고 실수해서 데이터가 유실될까봐 조심조심하다보니
시간도 그만큼 걸리더라구요...
그래서 한번
"자바를 이용해서 특정 폴더의 하위폴더의 내부 파일들을 특정폴더로 일괄 이동시키는 프로그램을 한번 개발해볼까?"
하는 생각으로 한번 개발해봤습니다.
프로그램명 : Move File
요구사항
현재 IT기업에서 근무하고 있는 A씨는 취미생활로 자료들을 수집하는데
수집하는 자료는 항상 (폴더 -> 파일 )이렇게 폴더로 한번 바인딩되어있어서 30개의 자료를 수집했다고 하면
30개의 폴더가 만들어져있다. 각 파일도 동일하게 30개의 파일이있다.
매번 손으로 하나하나 옮겨 자료의 유실도 발생하고 시간도 오래걸려 이 업무에 대해서 자동화를 하고자한다.
간단한 설계
- 폴더(디렉터리) 선택기능이 있어야한다.
- 폴더내부의 폴더들을 탐색한다.
- 폴더라고 판단된 경우 폴더로 진입하고 해당 파일을 이동한다.
- 이동이 완료된 리스트를 받아낸다.
실행결과
프로그래밍 상세
디렉터리 체크
String sourceDirectory = "C:\\ff"; // 원본 폴더 경로
File sourceDir = new File(sourceDirectory);
if (!sourceDir.isDirectory()) {
System.out.println("소스 디렉토리가 존재하지 않습니다.");
return;
}
우선은 하드코딩으로 내 C드라이브에있는 ff폴더에 대해서 실행하도록 작성했다.
나중에 추후 디렉터리를 선택할 수 있도록 수정하겠다.
선택한 파일의 내부 하위 디렉터리 체크
File[] subDirectories = sourceDir.listFiles(File::isDirectory);
if (subDirectories == null || subDirectories.length == 0) {
System.out.println("하위 디렉토리가 없습니다.");
return;
}
여기서 sourceDir.listFiles(File::isDirectory); 이걸 사용했는데 이것은 람다식을 이용했습니다. 아래 설명입니다.
File 클래스의 isDirectory() 메서드를 참조하는 방식이며, 이렇게 작성된 람다식은 File 객체가 디렉토리인지 여부를 판별하는 함수로서 사용됩니다.
다시 요약하자면, sourceDir.listFiles(File::isDirectory)는 sourceDir 디렉토리 내에 있는 하위 항목들 중에서 디렉토리인 것들만을 필터링하여 배열로 반환하는 역할을 합니다. 이 배열은 결과적으로 File 객체들을 포함하게 됩니다.
내부디렉터리 파일 체크 후 파일 이동
for (File subDir : subDirectories) {
File[] videoFiles = subDir.listFiles((dir, name) -> name.endsWith(".avi"));
if (videoFiles != null && videoFiles.length > 0) {
for (File videoFile : videoFiles) {
String targetPath = sourceDirectory + File.separator + videoFile.getName();
boolean success = videoFile.renameTo(new File(targetPath));
if (success) {
System.out.println("파일 이동 성공: " + videoFile.getAbsolutePath());
} else {
System.out.println("파일 이동 실패: " + videoFile.getAbsolutePath());
}
}
}
}
여기 소스에서는 우선 파일명이 avi를 가지고 있는지를 확인하고 그것들을 따로 배열안에 넣어 파일의 유효성검사를 한뒤 포문을 돌려 파일을 이동하는 방법으로 작성했습니다.
targetPath 변수를 통해 선택한 디렉터리(이동시킬 디렉터리)로 지정해주었고
해당 파일을 renameTo 를 이용해 신규파일을 생성시키는 방법으로 작성했습니다.
그리고 성공유무를 변수로 처리하여 콘솔로 나타나게 했습니다.
* 만약 avi가 아니라면??? 이때 본인만의 필터링을 추가해주면 되겠습니다.
추가된 소스
// 지원할 동영상 확장자들 List<String> supportedFormats = Arrays.asList(".avi", ".mp4", ".mkv", ".mov", ".wmv"); for (File subDir : subDirectories) { File[] videoFiles = subDir.listFiles((dir, name) -> { String lowercaseName = name.toLowerCase(); return supportedFormats.stream().anyMatch(format -> lowercaseName.endsWith(format)); }); if (videoFiles != null && videoFiles.length > 0) { for (File videoFile : videoFiles) { String targetPath = sourceDirectory + File.separator + videoFile.getName(); boolean success = videoFile.renameTo(new File(targetPath)); if (success) { System.out.println("파일 이동 성공: " + videoFile.getAbsolutePath()); } else { System.out.println("파일 이동 실패: " + videoFile.getAbsolutePath()); } } } }
소스설명 :
각 동영상 확장자들을 list로 생성하고
디비오 파일즈 라는 문자열배열객체에 추가할때 하위폴더내의 파일들이 동영상확장자로 되어있는지 확인하고 추가하는 로직이다.
중간 테스트 단계
이제 작성한 소스들이 잘작동하는지 테스트를 해보겠습니다.
D:\희안\moview\ 라는 폴더안에 3개의 폴더가있습니다. 각 폴더내부에는
위와같이 각각의 파일들이 있습니다.
실행
콘솔로 정상적으로 실행되었다고 나왔습니다.
그리고 폴더들도 모두 이동되었습니다. 현재 내용이 없기에 따로 열어서 확인은 불가합니다.
JFrame을 이용해서 프로그램처럼 보이게하기
이제 마지막으로 JFrame을 이용해서 나름 프로그램처럼 보이게 해보겠습니다!
UI 상세
- 400*500 직사각형 프레임
- 폴더위치변환기라는 문구가있어여함
- 폴더선택버튼을 통해 폴더선택 가능
- 선택폴더에 대해 한줄로 나와야함
- 변환하기 버튼을 통해 함수실행
- 성공한 파일명들이 하단에 나타나서 성공유무를 알 수 있어야한다.
소스상세
public static void main(String[] args) {
JFrame frame = new JFrame("Move File And Delete Folder Program");
frame.setPreferredSize(new Dimension(400, 500));
JLabel label = new JLabel("Move File And Delete Folder");
label.setFont(new Font("Arial", Font.BOLD, 20));
label.setHorizontalAlignment(JLabel.CENTER);
JButton selectButton = new JButton("폴더 선택");
JTextArea resultArea = new JTextArea(10, 15);
JButton renameButton = new JButton("변환하기!");
JLabel convertedListLabel = new JLabel("변환된 리스트:");
convertedListLabel.setHorizontalAlignment(JLabel.LEFT);
selectButton.addActionListener(e -> {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int option = fileChooser.showOpenDialog(frame);
if (option == JFileChooser.APPROVE_OPTION) {
File folder = fileChooser.getSelectedFile();
resultArea.setText(folder.getAbsolutePath());
renameButton.setEnabled(true);
}
});
renameButton.addActionListener(e -> {
String folderPath = resultArea.getText();
if (!folderPath.isEmpty()) {
File folder = new File(folderPath);
moveFile(folder, convertedListLabel);
}
});
List<JComponent> componentList = Arrays.asList(label, selectButton, resultArea, renameButton,
convertedListLabel);
frame.getContentPane().add(createMainPanel(componentList));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null); // 화면 정중앙에 위치
frame.setVisible(true);
}
public static JPanel createMainPanel(List<JComponent> arr) {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(4, 1, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
arr.forEach(component -> panel.add(component));
return panel;
}
private static void moveFile(final File sourceDirectory, JLabel convertedListLabel) {
List<String> supportedFormats = Arrays.asList(".avi", ".mp4", ".mkv", ".mov", ".wmv");
File sourceDir = sourceDirectory;
List<String> convertedFiles = new ArrayList<>();
if (!sourceDir.isDirectory()) {
convertedListLabel.setText("선택한 경로는 폴더가 아닙니다.");
return;
}
File[] subDirectories = sourceDir.listFiles(File::isDirectory);
if (subDirectories == null || subDirectories.length == 0) {
convertedListLabel.setText("하위 디렉토리가 없습니다.");
return;
}
for (File subDir : subDirectories) {
File[] videoFiles = subDir.listFiles((dir, name) -> {
String lowercaseName = name.toLowerCase();
return supportedFormats.stream().anyMatch(format -> lowercaseName.endsWith(format));
});
if (videoFiles != null && videoFiles.length > 0) {
for (File videoFile : videoFiles) {
String targetPath = sourceDirectory.getPath() + File.separator + videoFile.getName();
boolean success = videoFile.renameTo(new File(targetPath));
if (success) {
convertedFiles.add("파일 성공 : " + videoFile.getName());
} else {
convertedFiles.add("파일 이동 실패 : " + videoFile.getName());
}
}
}
}
displayConvertedFiles(convertedFiles, convertedListLabel);
}
private static void displayConvertedFiles(List<String> convertedFiles, JLabel convertedListLabel) {
StringBuilder fileList = new StringBuilder("<html>변환된 파일 리스트:<br>");
for (String filePath : convertedFiles) {
fileList.append(filePath).append("<br>");
}
fileList.append("</html>");
convertedListLabel.setText(fileList.toString());
}
우선 메인함수(실행부)에 제이프레임을 넣었습니다.
전역변수로 따로 빼서 각 셋팅부를 각 함수로 넣고싶었으나, 좀더 빠르게 프로그램을 개발하고 포스팅을 하고
저도 직접 사용해보고 싶어서 우선은 메인함수에서 진행되도록 했습니다.
위 소스에 대해 디테일 설명
폴더선택버튼이벤트
selectButton.addActionListener(e -> {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int option = fileChooser.showOpenDialog(frame);
if (option == JFileChooser.APPROVE_OPTION) {
File folder = fileChooser.getSelectedFile();
resultArea.setText(folder.getAbsolutePath());
renameButton.setEnabled(true);
}
});
이 부분은 디렉터리 선택을 할 수있는 버튼에 대한 이벤트를 넣어주는 람다형식 함수입니다. 자바스크립트의 addEventListener처럼 이해하시면 편합니다. 그리고 여기 선택된 디렉터리도 해당 경로가 표기되도록 작성했습니다.
변환하기버튼이벤트
renameButton.addActionListener(e -> {
String folderPath = resultArea.getText();
if (!folderPath.isEmpty()) {
File folder = new File(folderPath);
moveFile(folder, convertedListLabel);
}
});
이동시키는 버튼입니다. 동일하게 람다식을 이용했습니다. 폴더 경로를 가져오는 방법으로는 선택된 다렉터리의 경로가 나타나는 문구를 통해 진행합니다.
그리고 해당 경로를 폴더객체로 만든뒤 moveFile이라는 함수를 호출하여 파일 이동 로직을 수행하게끔 합니다.
패널추가함수
public static JPanel createMainPanel(List<JComponent> arr) {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(4, 1, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
arr.forEach(component -> panel.add(component));
return panel;
}
위 부분은 한번 forEach함수를 써보고 싶어서 따로 뺐습니다. 메인프레임에 들어갈 소스가
JPanel panel = new JPanel();
panel.addAncestorListener(null);
panel.setLayout(new GridLayout(4, 1, 10, 10));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.add(label);
panel.add(selectButton);
panel.add(resultArea);
panel.add(renameButton);
panel.add(convertedListLabel);
이렇게 길어서 어짜피 다들어가는것 한번 리팩토링을 해보자! 라는 생각으로 작성했습니다. 큰의미는 없습니다.
파일이동 함수
private static void moveFile(final File sourceDirectory, JLabel convertedListLabel) {
List<String> supportedFormats = Arrays.asList(".avi", ".mp4", ".mkv", ".mov", ".wmv");
File sourceDir = sourceDirectory;
List<String> convertedFiles = new ArrayList<>();
if (!sourceDir.isDirectory()) {
convertedListLabel.setText("선택한 경로는 폴더가 아닙니다.");
return;
}
File[] subDirectories = sourceDir.listFiles(File::isDirectory);
if (subDirectories == null || subDirectories.length == 0) {
convertedListLabel.setText("하위 디렉토리가 없습니다.");
return;
}
for (File subDir : subDirectories) {
File[] videoFiles = subDir.listFiles((dir, name) -> {
String lowercaseName = name.toLowerCase();
return supportedFormats.stream().anyMatch(format -> lowercaseName.endsWith(format));
});
if (videoFiles != null && videoFiles.length > 0) {
for (File videoFile : videoFiles) {
String targetPath = sourceDirectory.getPath() + File.separator + videoFile.getName();
boolean success = videoFile.renameTo(new File(targetPath));
if (success) {
convertedFiles.add("파일 성공 : " + videoFile.getName());
} else {
convertedFiles.add("파일 이동 실패 : " + videoFile.getName());
}
}
}
}
displayConvertedFiles(convertedFiles, convertedListLabel);
}
여긴 디렉터리 체크 및 이동에 대한 소스 설명은 생략하겠습니다.
그러나 나중에 라벨에 성공유무에 따른 처리결과를 리스트로 만들어서 그것을 작동시키게하는 함수를 마지막에 선언했습니다.
결과나타내는 함수
private static void displayConvertedFiles(List<String> convertedFiles, JLabel convertedListLabel) {
StringBuilder fileList = new StringBuilder("<html>변환된 파일 리스트:<br>");
for (String filePath : convertedFiles) {
fileList.append(filePath).append("<br>");
}
fileList.append("</html>");
convertedListLabel.setText(fileList.toString());
}
리스트를 넘겨받아 라벨에 표기되도록 했습니다.
html로 하는 이유는 깔끔하게 줄내리기가 먹힐수 있도록 하기위해서 했습니다.
실행결과는 맨상단에 있습니다.
이것으로 파일과 Jframe을 이용한 폴더 및 파일 결로 이동시키는 프로그램을 만들어 보았습니다.
긴 글 읽어주셔서 감사합니다!
'프로젝트 > 자바로 간단하게 프로그래밍!' 카테고리의 다른 글
[파일]선택한 폴더 내의 파일들의 이름을 일괄 변경하는 프로그램 (0) | 2023.08.22 |
---|
댓글