1.``smallFileInputfromat类
看一下整体代码
首先我们根据需求。你要定义一个 Fire input format要先继承一个 Fireinputformat。继承 File input format需要你实现一个record read的方法。你把它继承之后,你就需要实现。 Record reader方法。
由于我们刚才分析要对小文件进行统一处理,所以我们设置了不可分割。如果他进行分割,他就不能够进行统一处理了,所以我们把它设置成统一处理。
然后呢,你这里需要自定义一个 Record reader对象。所以我们给他自定义一个 Record reader对象然后来继承 Record reader,然后呢,我们把这个 Split和context信息给他传了进去,嗯,最后返回 Record reader对象。
然后file input format就已经完成了
在这里插入代码片package com.jinghang.class30.smallFile;
import org.apache.hadoop.fs.Path;
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;
public class smallFileInputfromat extends FileInputFormat<Text,Text> {
@Override
protected boolean isSplitable(JobContext context, Path filename) {
return false;
}
@Override
public RecordReader<Text, Text> createRecordReader(InputSplit Split, TaskAttemptContext Context) throws IOException, InterruptedException {
// return null;
smallFileRecordReader reader = new smallFileRecordReader();
reader.initialize(Split,Context);
return reader;
}
}
2.smallFileRecordReader 类
File input format完成之后,我们就开始自己来写这Record reader对象,然后继承之后发现它导入了几个方法:
初始化方法 ,
返回下一组kv,
获取下一个Key,
获取下一个value,
显示程序进程,
关闭程序,
然后我们处理的时候,对于这一块显示程序进程,文件要么读完,要么没读完。
所以我们设置一个布尔值,默认他是没有读。
然后我们在显示程序进程那里反馈说,如果读完返回0没有读完,返回1。
然后在初始化方法中,
把inputsplit对象 Split转化为 FileSplit,就是把切片信息转化为文件切片信息,
然后通过file split.getPath()就获取到了文件路径,
然后获取的文件路径之后,我们开始下一步操作,首先先判断一下我们文件有没有读 ,
如果没有读,我们开始我们的逻辑,我们的默认值是true,就是没有读,
首先我们上面已经获取到了文件的路径,这时候Path.get name(),也可以获取到文件里。这个时候我们 Key就已经有了。
我们value值是文件内容,然后这样呢,我们就学到了一个新的东西,一个固定的用法。就是已知一个文件名和文件路径,然后再通过一个装箱的方法。 Buffer reader里面装着input stream reader, Input stream reader里面又装着fireinputStream,通过这种装箱的方法,最终拿到了文件的所有内容就是 Buffered reader
然后我们定义一个line接收它的每行内容,
定义一个新的Stringbuffer接收新的每行内容,
然后门的循环
Buffer reader每次读取一行。然后把它复制给line然后通过Stringutils来判断他是不是读完。如果没有读完的话,我每次都去银行把它加到StringBuffer里面,然后这个循环走完,
Stringbuffer里面就有了所有的文件内容。然后把它写到v里面, 接着全部读完之后,把他的状态改为 False, Return true返回,最终输入结果。
最后。 Return false。
这里的return false是接着判断,还有内容的话我可以接着判断。
这个写完之后获取下一个K。
在写完之后获取下一个value。
然后获取进程,要么成功要么失败。
关闭程序可以不用写。
然后record reader主体的代码写完。
我们写了一下Driver程序
在这里插入代码片
package com.jinghang.class30.smallFile;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.RecordReader;
import org.apache.hadoop.mapreduce.TaskAttemptContext;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class smallFileRecordReader extends RecordReader<Text, Text> {
// 定义一个变量表示 进程是否结束
private boolean isNotRead = true;
//定义一个变量表示 文件路径名
private Path path = null;
//定义一个变量 key 表示 文件路径名+文件名
private Text key = new Text();
//定义一个变量 v表示 文件内容
private Text v = new Text();
//初始化方法
@Override
public void initialize(InputSplit split, TaskAttemptContext context) throws IOException, InterruptedException {
//将切片信息转换为文件切片信息
FileSplit file = (FileSplit) split;
//拿到文件路径
path = file.getPath();
}
//返回下一组KV对
@Override
public boolean nextKeyValue() throws IOException, InterruptedException {
if (isNotRead) {
//如果还没有读取文件,则开始处理文件
//key 文件路径和文件名 value 是文件内容
//1.获取key值
key.set(path + path.getName());//path表示文件路径,path.getName()表示文件名
//2.。获取value值
//2.1获取文件内容的固定用法
BufferedReader bufferedReader = new BufferedReader(new
InputStreamReader(new FileInputStream(path.toString().replace("file:/",""))));
//2.2读取行内容
String line;//定义一个变量接收value值每一行数据
//定义一个StringBuffer接收bufferedReader里面所有内容
StringBuffer stringBuffer = new StringBuffer();
//StringUtils.isNotEmpty判断内容是否为空的工具
//line = bufferedReader.readLine()每次读取一行内容赋值给line
//stringBuffer.append(line+"======") 把每一行内容追加到stringBuffer,
// 加=为了区分每一行内容
while (StringUtils.isNotEmpty(
line = bufferedReader.readLine())) {
stringBuffer.append(line+"======");
}
//把内容写入到 v里面
this.v.set(stringBuffer.toString());
// this.isNotRead=false代表内容已经读完
this.isNotRead=false;
return true;
}
return false;
}
//获取下一个Key
@Override
public Text getCurrentKey() throws IOException, InterruptedException {
//this.key 获取下一个Key
return this.key;
}
//获取下一个Value
@Override
public Text getCurrentValue() throws IOException, InterruptedException {
//this.v 获取下一个Value
return this.v;
}
//显示程序进程
@Override
public float getProgress() throws IOException, InterruptedException {
return isNotRead ? 0 : 1;
}
//关闭程序
@Override
public void close() throws IOException {
}
}
3.smallFileDriver 类
Driver 程序
设置一个变量接收文件,输入输出路径。
获取配置文件。
根据配置文件获取job实例。
关联驱动了。
设置输入类型。
设置文件,最终输出格式。
设置K和value最终输出类型。
设置文件,输入输出路径。
提交。
在这里插入代码片
```package com.jinghang.class30.smallFile;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import java.io.IOException;
public class smallFileDriver {
public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
//设置一个变量接收文件输入输出路径
args = new String[]{"E:\\data\\input", "E:\\data\\output\\small"};
//获取配置文件
Configuration conf = new Configuration();
//1.获取job实列
Job job = Job.getInstance(conf);
//2.关联驱动类
job.setJarByClass(smallFileDriver.class);
//3.设置输入类型
job.setInputFormatClass(smallFileInputfromat.class);
//4.设置文件最终输出格式
job.setOutputFormatClass(SequenceFileOutputFormat.class);
//5.设置key和value最终输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//6。设置文件输入输出路径
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//7.提交
boolean b = job.waitForCompletion(true);
//安全退出,0 或者 1
System.out.println(b ? 0 : 1);
}
}