我想使用BufferedReader来读取上传到我服务器的文件。
该文件将被写为CSV文件,但我不能假设这一点,所以我编写了一些测试,其中文件是图像或二进制文件(假设客户端向我发送了错误的文件或攻击者是试图破坏我的服务),或者更糟糕的是,该文件是一个有效的CSV文件,但有100MB的行。
我的应用程序可以处理这个问题,但它必须读取文件的第一行:
...
String firstLine = bufferedReader.readLine();
//Perform some validations and reject the file if it's not a CSV file
...
但是,当我编写一些测试代码时,我发现了一个潜在的风险:BufferedReader在找到返回行之前不会对它读取的字节数执行任何控制,因此它最终会抛出OutOfMemoryError。 / p>
这是我的测试:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import org.junit.Test;
public class BufferedReaderTest {
@Test(expected=OutOfMemoryError.class)
public void testReadFileWithoutReturnLineCharacter() throws IOException {
BufferedReader bf = new BufferedReader(getInfiniteReader());
bf.readLine();
bf.close();
}
private Reader getInfiniteReader() {
return new Reader(){
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return 'A';
}
@Override
public void close() throws IOException {
}
};
}
}
我一直在互联网上查找一些安全的BufferedReader实现,但我找不到任何东西。我发现的唯一一个类是来自apache IO的BoundedInputStream
,它限制了输入流读取的字节数。
我需要BufferedReader的实现,它知道如何限制每行中读取的字节/字符数。
这样的事情:
有人知道有这种行为的BufferedReader的实现吗?
答案 0 :(得分:2)
这不是你应该如何检测文件是否是二进制文件。
以下是检查文件是否为真文本的方法;请注意,这要求您事先知道编码:
final Charset cs = StandardCharsets.UTF_8; // or another
final CharsetDecoder decoder = cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT); // default is REPLACE!
// Here, "in" is the input stream from the file
try (
final Reader reader = new InputStreamReader(in, decoder);
) {
final char[] buf = new char[4096]; // or other size
while (reader.read(buf) != -1)
; // nothing
} catch (MalformedInputException e) {
// cannot decode; binary, or wrong encoding
}
现在,由于您可以在BufferedReader
上初始化Reader
,因此您可以使用:
try (
final Reader r = new InputStreamReader(in, decoder);
final BufferedReader reader = new BufferedReader(r);
) {
// Read lines normally
} catch (CharacterCodingException e) {
// Not a CSV, it seems
}
// etc
现在,关于它是如何工作的更多解释......虽然这是用Java阅读文本的基础部分,但它是一个同样从根本上被误解的部分!
当您使用Reader
将文件作为文本阅读时,您必须指定字符编码;在Java中,这是Charset
。
内部发生的事情是Java将从CharsetDecoder
创建Charset
,阅读byte
流并输出char
流。有三种方法可以处理错误:
CodingErrorAction.REPLACE
(默认):不可映射的字节序列被Unicode replacement character替换(它响铃,对吧?); CodingErrorAction.IGNORE
:不可映射的字节序列不会触发char
的发射; CodingErrorAction.REPORT
:不可映射的字节序列触发抛出CharacterCodingException
,继承IOException
;反过来,CharacterCodingException
的两个子类是MalformedInputException
和UnmappableCharacterException
。因此,为了检测文件是否是真正的文本,您需要做的是:
CharsetDecoder
配置的CodingErrorAction.REPORT
; InputStreamReader
。这是一种方式;还有其他人。但是,所有这些都会在某个时刻使用CharsetDecoder
。
同样,反向操作有CharsetEncoder
(char
流到byte
流),这是Writer
系列使用的内容。
答案 1 :(得分:0)
谢谢@fge的答案。我最终实现了一个安全的Reader
,它可以处理行太长(或根本没有行)的文件。
如果有人想查看代码,可以在这里找到项目(非常小的项目,即使是很多测试):