用一个案例来讲述怎样自定义实现InputFormat和OutputFormat
需求:
将多个小文件合并成一个SequenceFile文件
(SequenceFile文件是Hadoop用来存储二进制形式的key-value对的文件格式,SequenceFile里面存储着多个文件,存储的形式为文件路径+名称为key,文件内容为value)
package com.jee.inputformat;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.BytesWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.JobContext;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import java.io.IOException;
// 直接继承FileInputFormat而不是InputFormat
// 原因是如果继承InputFormat 我们需要重写两个方法 一个是切片方法 另一个是将读取的内容转化成key-value
// 而我们之间继承InputFormat的子类FileInputFormat 它已经实现了切片方法(就是默认的FIF切片方法) 在我们这个案例也适用 我们只需要实现将读取的内容转化成key-value即可
// FileInputFormat的参数类型 是map任务接受到的类型 根据案例要求是 文件名和文件内容
// 所以参数适用 Text 和 BytesWritable (因为有些文件不一定是字符串 所以最好不使用Text 而是用BytesWritable 字节)
public class WholeFileInputFormat extends FileInputFormat<Text, BytesWritable> {
//根据需求 我们的文件 不论大小 都不能被切片 直接返回false
@Override
protected boolean isSplitable(JobContext context, Path filename) {
return false;
}
public RecordReader createRecordReader(InputSplit inputSplit, TaskAttemptContext taskAttemptContext) throws IOException, InterruptedException {
return new WholeFileRecordReader();
}
}
package com.jee.inputformat;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.a