서론
지난 포스트에서 파일을 위에서 아래로 읽어가며 분할하는 방법을 알아보았다. 이번 포스트에서는 반대로 아래에서 위로 올라가며 파일을 분할하는 방법을 다루어보려고 한다. 위에서 아래로 읽어서 분할하는 방식은 과거 데이터를 읽을 때는 좋겠지만 과거보다는 최신일자의 데이터를 확인하는 경우가 일상다반사이기 때문에 이번에 다루는 내용이 좀 더 활용도가 높다고 생각한다.
소스코드
1. FileUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
|
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class FileUtils {
/* Singleton */
public static FileUtils instance = new FileUtils();
/* 생성자1 */
public static FileUtils getInstance() {
if( instance != null ) {
return instance;
}
return new FileUtils();
}
/**
* 저장용량 단위별 처리량을 반환
* @param unit 저장 용량 단위
* @return 저장 용량 단위별 처리량
*/
private static int getUnitCapacity(String unit) {
if( unit == null ) { return 1; }
unit = unit.toUpperCase();
Map<String, Integer> units = new HashMap<String, Integer>();
units.put("B", 1);
units.put("K", 1024);
units.put("M", 1024*1024);
units.put("G", 1024*1024*1024);
return units.get(unit);
}
/**
* 파일 저장
* @param filename 저장할 파일명(경로가 없을 경우 현재 경로에 저장)
* @param text 파일에 저장할 내용
* @param append 이어쓰기 모드여부. true: 이어쓰기, false: 다시쓰기
* @param encoding 인코딩
* @return 파일이 저장된 경로를 반환
*/
public static String saveFile(String filename, String text, boolean append, Charset encoding) {
OutputStreamWriter writer = null;
File outfile = null;
FileOutputStream outfile_stream = null;
String savePath = null;
try {
outfile = new File(filename);
outfile_stream = new FileOutputStream(outfile, append);
writer = new OutputStreamWriter(outfile_stream, encoding);
writer.write(text);
writer.close();
savePath = outfile.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if( writer != null ) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return savePath;
}
/**
* 파일 내용을 byte[]로 받으며, 내용은 UTF8로 저장
*/
public static String saveFileUTF8(String filename, byte[] text, boolean append) {
return saveFile(filename, new String(text), append, StandardCharsets.UTF_8);
}
/**
* 파일 내용을 String로 받으며, 내용은 UTF8로 저장
*/
public static String saveFileUTF8(String filename, String text, boolean append) {
return saveFile(filename, text, append, StandardCharsets.UTF_8);
}
/**
* 파일을 아래에서 위로 읽음
* @param file 읽어드릴 파일 객체
* @param size 출력할 사이즈
* @param unit B: 바이트(8Bit), K: 킬로바이트(1024B), M: 메가파이트(1024KB)
* @param split_cnt 분할 파일 최대 개수
* @return 마지막으로 조회된 데이터
*/
public static byte[] splitFileReverse(File file, int size, String unit, int split_cnt) {
final String PREFIX_DIR = "";
final String PREFIX_FILENAME = "splited_r";
final String PREFIX_EXT = "log";
RandomAccessFile reader = null;
byte[] buffer = null;
try {
// Read file
reader = new RandomAccessFile(file, "r");
// Variables
int interval = size * getUnitCapacity(unit);
long EOF = reader.length();
int spos = (int)EOF;
int epos = (int)EOF;
String filename = null;
String savepath = null;
//파일명 역순처리
float gab = (float)EOF/interval;
float mok = (int) gab;
float nmg = gab - mok;
if( nmg > 0 ) {
mok += 1;
}
int count = (int)mok+1;
do {
// Set Position
epos = spos;
spos = spos - interval;
count -= 1;
// BreakPoint
if( spos <= 0 && epos <= 0 ) {
break;
}
if( spos < 0 ) {
spos = 0;
}
// 커서 이동
reader.seek(spos);
// Read Text
buffer = new byte[epos-spos];
reader.readFully(buffer);
// Create New Flie
filename = PREFIX_DIR+PREFIX_FILENAME+"_"+count+"."+PREFIX_EXT;
savepath = saveFileUTF8(filename, buffer, false);
// 파일 저장경로
System.out.printf("savepath: %s, size: %d\n", savepath, buffer.length);
// 파일 최대건수
if( split_cnt != 0 && count >= split_cnt ) {
break;
}
} while( spos > 0 );
} catch ( Exception e ) {
e.printStackTrace();
} finally {
try {
if( reader != null ) {
reader.close();
reader = null;
}
} catch ( Exception e ) {
e.printStackTrace();
}
}
// 마지막에 읽은 부분만 반환
return buffer;
}
public static void main(String[] args) {
// TODO: arguments로 받아서 처리
String filepath = "C:\\logs\\20201021_stdout.log";
// 파일 객체 생성
File file = new File(filepath);
// 100MB 단위로 모두 분할
FileUtils.splitFileReverse(file, 100, "M", 0);
}
}
|
cs |
대분류
이전에 작성했던 소스코드랑 크게 다르지 않다. 다만, 중간에 파일을 읽을 커서의 위치를 역방향으로 계산했을 뿐이다. 이래서 RandomAccessFile이 다루기 편하다.
'Back-end > JAVA' 카테고리의 다른 글
[JAVA] Quartz 스케줄러 만들기 (1) - 실행 (0) | 2020.11.09 |
---|---|
[JAVA] 간단한 HTTP 서버 구축하기 (0) | 2020.11.05 |
[JAVA] 파일 분할 - 용량 단위, 위에서 아래로 ↓ (0) | 2020.10.22 |
[크롤링] Selenium을 이용한 JAVA 크롤러 (2) - Jsoup과 비교 (With. Twitter) (0) | 2020.02.28 |
[크롤링] Selenium을 이용한 JAVA 크롤러 (1) - HTML 파싱 (0) | 2020.02.27 |
댓글