Skip to main content

2 posts tagged with "read"

View All Tags

· 4 min read

Java로 웹에 있는 영상 파일을 다운로드하는 기능을 간단하게 구현하면서 기본함수에 대한 의문이 생겼다.

실제로 영상 파일이 깨지는 문제도 있었다.

1. FileOutputStream write(byte[] b)

아래 구문에서 사용된 write 함수는 byte buffer, offset, length를 parameter로 받는다.

좀 더 간단한 형태인 write(byte[] b)를 사용하는 것으로 코드를 변경하면 문제가 발생한다. (테스트했던 영상파일들에서는)

InputStream is = conn.getInputStream();
FileOutputStream fos = new FileOutputStream(filename);
byte[] buffer = new byte[4096];
int readBytes = -1;
while((readBytes = is.read(buffer)) != -1) {
fos.write(buffer, 0, readBytes);
}

write(byte[] b)의 내용을 보자.

fileoutputstream-write(byte[])내용은 간단하다. byte 배열의 길이만큼 file output stream에 쓰는 기능이다.

2. 왜 문제가 생겼는가?

결과적으로 보자면 HTTP로 들어온 input stream을 읽어들인 byte 수가 byte array의 총 크기보다 작은 경우가 있기 때문이다.

보통의 파일을 읽으면 마지막만 제외하고 buffer로 사용하는 byte array를 꽉꽉 채웠었는데 이 경우는 그렇지 않았다.

궁금해서 살펴보니 HttpInputStream의 read(byte[])는 이렇게 구현되어 있다.

httpinputstream-read(byte[])read(byte[], int, int) 함수를 호출하지만 상속트리를 뒤지다보면 결국 자신이 구현한 read() 함수 호출이 기본이 된다.

read() 함수는 아래 그림처럼 -1이 나오기 전까지 1바이트씩 읽어들이도록 되어있고 read(byte[], int, int) 함수도 -1을 만나기 전까지는 byte array의 길이만큼 읽어들이게 된다.

httpinputstream-read()

그러니까 byte array 만큼 읽어들이지 못하는 경우가 있을 수 있는데 이에 대해서 고려하지 못했던게 문제였다.

실제로 찍어보면 사용된 byte array는 4096만큼 0으로 초기화되어 있고 계속 사용하기 때문에 byte array의 길이는 4096으로 항상 동일하고 읽어들인 실제 byte 수는 그것보다 작은 경우가 지속적으로 발생했다.

이런 상태에서 FileOutputStream의 write(byte[]) 함수를 사용하면 byte array의 길이만큼 쓰게된다. 실제 읽어들인 byte 만큼이 아니라 과거에 사용했던 일부 byte가 섞이게 되는 거다.

3. 결론

가능하면 write(byte[]) 함수는 사용하지 않을 생각이다.

조금 불편해보여도 확실한게 낫다.

· 4 min read

JSP로 간단한 페이지를 만드는데 로컬에 있는 로그파일을 읽어서 화면에 보여주기로 했다.

이미 WAS의 context path 안에 있는 파일이고 txt 라서 url 호출만으로도 빠르게 파일 내용을 보여주는데는 문제가 없었는데, 정말 raw 파일이라서 보기도 힘들고 예쁘지 않아 table 안에 넣어서 보여주기로 결정했는데 단순하게 BufferedReader class에 있는 readline을 사용해보니 너무 느렸다.

속도도 문제였지만 로그파일이 보통 1MB 이상이고 반복적인 로딩이 예정된 상태라 과부하를 신경쓰지 않을 수 없는 상태여서 다른 방안을 생각할 수 밖에 없었다.

readline 함수는 BufferedReader 라는 클래스에 정의된 함수로 유사한 함수들이 많다. 그 중 read 라는 함수가 있는데 정의를 살펴봤는데, 우선 BufferedReader는 Reader 클래스를 상속한 함수로 Reader 클래스에 정의된 간단한 형태의 read(char[] cbuf)는 read(char[] cbuf, int off, int len)을 호출하도록 정의되어 있다. 그리고 read(char[] cbuf, int off, int len)은 abstract 함수로 각각의 서브클래스에서 정의되는 형태이다.

그래서 BufferedReader 클래스의 read와 readline을 비교해 본 결과 아래와 같은 차이를 발견할 수 있었다.

- read는 loop 내부의 함수 호출이 작다.

- readline은 loop 내부의 함수 호출이 많다

- read는 system 함수나 원시 함수 위주의 호출

- readline은 read 보다 상위의 함수나 클래스 사용

- read는 buffer size 지정하지 않았을 때 기본 8K를 한꺼번에 읽어들임

- readline은 size와 관계없이 line feed까지만 읽어들임

 

요약해보면, readline은 무조건 linefeed를 기준으로 읽어들이는데 동작 자체가 무겁고 read 함수는 정의된 block 단위로 읽어들이는데 정의된 동작 자체가 가볍다. 당연히 line이 많아질수록 readline 함수는 반복횟수가 많아질 수 밖에 없는 구조로 보이는데 이런 단순한 차이가 실제로는 체감할 수 있는 엄청난 차이를 가져오는 것 같다.

 

구현에 있어서 편의성과 가독성, 성능을 모두 고려해야 하겠지만, 그리고 고려하겠지만, 무작정 습관적인 사용과 맹목적인 신뢰는 언제나처럼 금기시해야 한다는 걸 느낀다.