본문 바로가기
Back-end/JAVA

[JAVA] 파일 분할 - 용량 단위, 아래에서 위로 ↑

by 허도치 2020. 10. 22.
서론

  지난 포스트에서 파일을 위에서 아래로  읽어가며 분할하는 방법을 알아보았다. 이번 포스트에서는 반대로 아래에서 위로 올라가며 파일을 분할하는 방법을 다루어보려고 한다. 위에서 아래로 읽어서 분할하는 방식은 과거 데이터를 읽을 때는 좋겠지만 과거보다는 최신일자의 데이터를 확인하는 경우가 일상다반사이기 때문에 이번에 다루는 내용이 좀 더 활용도가 높다고 생각한다.

 

 

 

소스코드
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이 다루기 편하다.

댓글