我编写了一个自定义记录阅读器来读取Hadoop中的文本和gzip文件,因为我特别要求将完整的文件数据作为密钥的值和文件名。来源如下:
public class WholeFileRecordReader extends RecordReader<Text, BytesWritable> {
private CompressionCodecFactory compressionCodecs = null;
private FileSplit fileSplit;
private Configuration conf;
private InputStream in;
private Text key = new Text("");
private BytesWritable value = new BytesWritable();
private boolean processed = false;
@Override
public void initialize(InputSplit split, TaskAttemptContext context)
throws IOException, InterruptedException {
this.fileSplit = (FileSplit) split;
this.conf = context.getConfiguration();
final Path file = fileSplit.getPath();
compressionCodecs = new CompressionCodecFactory(conf);
final CompressionCodec codec = compressionCodecs.getCodec(file);
System.out.println(codec);
FileSystem fs = file.getFileSystem(conf);
in = fs.open(file);
if (codec != null) {
in = codec.createInputStream(in);
}
}
@Override
public boolean nextKeyValue() throws IOException, InterruptedException {
if (!processed) {
byte[] contents = new byte[(int) fileSplit.getLength()];
Path file = fileSplit.getPath();
key.set(file.getName());
try {
IOUtils.readFully(in, contents, 0, contents.length);
value.set(contents, 0, contents.length);
} finally {
IOUtils.closeStream(in);
}
processed = true;
return true;
}
return false;
}
@Override
public Text getCurrentKey() throws IOException, InterruptedException {
return key;
}
@Override
public BytesWritable getCurrentValue() throws IOException, InterruptedException {
return value;
}
@Override
public float getProgress() throws IOException {
return processed ? 1.0f : 0.0f;
}
@Override
public void close() throws IOException {
// Do nothing
}
}
问题是我的代码正在读取不完整的文件数据。这可能是因为我使用fileSplit(指向压缩文件)来确定内容的长度,因此我得到一个较小的值。因此,这会导致将不完整的数据传递给Mapper。
有人可以指出我如何获取gizip文件数据的实际长度或修改RecordReader以便它读取完整的数据。
答案 0 :(得分:1)
延伸@Chris White的答案,我不得不对他给出的代码做出某些语法上的修改。它如下:
fileLength = (int) fileSplit.getLength();
compressionCodecs = new CompressionCodecFactory(conf);
final CompressionCodec codec = compressionCodecs.getCodec(file);
FileSystem fs = file.getFileSystem(conf);
in = fs.open(file);
if (codec != null) {
if (codec instanceof GzipCodec) {
byte[] len = new byte[4];
try {
in.skip(fileLength - 4);
IOUtils.readFully(in, len, 0, len.length);
fileLength = (len[3] << 24) | (len[2] << 16) + (len[1] << 8) + len[0];
} finally {
in.close();
}
}
in = fs.open(file);
in = codec.createInputStream(in);
}
非常感谢@Chris White的投入。没有你就不可能做到:)。
答案 1 :(得分:0)
对于GZip文件,您可以跳到最后4个字节(根据规范),应该返回原始的未压缩文件大小)。请注意,该值的模数为2 ^ 32,因此如果您希望原始文件大于此值,请务必小心。
所以你的初始化方法可以修改为类似的东西(未经测试!):
final CompressionCodec codec = compressionCodecs.getCodec(file);
System.out.println(codec);
FileSystem fs = file.getFileSystem(conf);
in = fs.open(file);
length = fileSplit.getLength();
if (codec instanceof GZipCodec) {
// skip to last 4 bytes
in.seek(length-4);
// read size
length = in.readInt();
// reset stream position
in.seek(0);
}
现在您应该拥有实际文件长度(对于未压缩和压缩的Gzip),您可以在nextKeyValue()方法中使用它。