1.环境准备

1.讲centos的hadoop安装包解压到windows桌面
2.添加环境变量
    HADOOP_HOME:E:\03-software_java\04-hadoop\hadoop-2.6.0-cdh5.9.0
    path:%HADOOP_HOME%/bin
3.把winutils.exe放在hadoop/bin文件下
    https://github.com/steveloughran/winutils   下载地址
4.更改NativeIO.java源码
    public static boolean access(String path, AccessRight desiredAccess)
      throws IOException {
//    return access0(path, desiredAccess.accessRight());   // 直接返回 true
    return true;
    }
    在项目目录创建一个的目录文件 org.apache.hadoop.io.nativeio
    创建 NativeIO.java 把源码复制过来更改

2.FileUtilsDelete.java    // 删除文件

package com.imooc.bigdata.hadoop.mr.wc;

import java.io.File;

public class FileUtilsDelete {
    /**
     * 删除文件,可以是文件或文件夹
     *
     * @param fileName:要删除的文件名
     * @return 删除成功返回true,否则返回false
     */
    public static boolean delete(String fileName) {
        File file = new File(fileName);
        if (!file.exists()) {
            System.out.println("删除文件失败:" + fileName + "不存在!");
            return false;
        } else {
            if (file.isFile())
                return deleteFile(fileName);
            else
                return deleteDirectory(fileName);
        }
    }

    /**
     * 删除单个文件
     *
     * @param fileName:要删除的文件的文件名
     * @return 单个文件删除成功返回true,否则返回false
     */
    public static boolean deleteFile(String fileName) {
        File file = new File(fileName);
        // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
        if (file.exists() && file.isFile()) {
            if (file.delete()) {
                System.out.println("删除单个文件" + fileName + "成功!");
                return true;
            } else {
                System.out.println("删除单个文件" + fileName + "失败!");
                return false;
            }
        } else {
            System.out.println("删除单个文件失败:" + fileName + "不存在!");
            return false;
        }
    }

    /**
     * 删除目录及目录下的文件
     *
     * @param dir:要删除的目录的文件路径
     * @return 目录删除成功返回true,否则返回false
     */
    public static boolean deleteDirectory(String dir) {
        // 如果dir不以文件分隔符结尾,自动添加文件分隔符
        if (!dir.endsWith(File.separator))
            dir = dir + File.separator;
        File dirFile = new File(dir);
        // 如果dir对应的文件不存在,或者不是一个目录,则退出
        if ((!dirFile.exists()) || (!dirFile.isDirectory())) {
            System.out.println("删除目录失败:" + dir + "不存在!");
            return false;
        }
        boolean flag = true;
        // 删除文件夹中的所有文件包括子目录
        File[] files = dirFile.listFiles();
        for (int i = 0; i < files.length; i++) {
            // 删除子文件
            if (files[i].isFile()) {
                flag = deleteFile(files[i].getAbsolutePath());
                if (!flag)
                    break;
            }
            // 删除子目录
            else if (files[i].isDirectory()) {
                flag = deleteDirectory(files[i].getAbsolutePath());
                if (!flag)
                    break;
            }
        }
        if (!flag) {
            System.out.println("删除目录失败!");
            return false;
        }
        // 删除当前目录
        if (dirFile.delete()) {
            System.out.println("删除目录" + dir + "成功!");
            return true;
        } else {
            return false;
        }
    }

    public static void main(String[] args) {
        //测试
        String dir = "C:/Users/Administrator/Downloads/a";
        delete(dir);
    }

}

3.WordCountMapper.java    // mapper逻辑处理

package com.imooc.bigdata.hadoop.mr.wc;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    /**
     * KEYIN: Map任务读数据的key类型,offset,是每行数据起始位置的偏移量,Long
     * VALUEIN:Map任务读数据的value类型,其实就是一行行的字符串,String
     * <p>
     * hello world welcome
     * hello welcome
     * <p>
     * KEYOUT: map方法自定义实现输出的key的类型,String
     * VALUEOUT: map方法自定义实现输出的value的类型,Integer
     * <p>
     * <p>
     * 词频统计:相同单词的次数 (word,1)
     * <p>
     * Long,String,String,Integer是Java里面的数据类型
     * Hadoop自定义类型:序列化和反序列化
     * <p>
     * LongWritable,Text
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String[] words = value.toString().split(" ");// 把value对应的行数据按照指定的分隔符拆开
        for (String word : words) {
            context.write(new Text(word), new IntWritable(1)); // 不区分大小写
//    context.write(new Text(word.toLowerCase()), new IntWritable(1)); // 区分大小写
        }

    }
}

4.WordCountReducer.java   // reducer逻辑处理

package com.imooc.bigdata.hadoop.mr.wc;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;
import java.util.Iterator;

public class WordCountReducer extends Reducer {
    /**
     * (hello,1)  (world,1)
     * (hello,1)  (world,1)
     * (hello,1)  (world,1)
     * (welcome,1)
     * <p>
     * map的输出到reduce端,是按照相同的key分发到一个reduce上去执行
     * <p>
     * reduce1: (hello,1)(hello,1)(hello,1)  ==> (hello, <1,1,1>)
     * reduce2: (world,1)(world,1)(world,1) ==> (world, <1,1,1>)
     * reduce3 (welcome,1)  ==> (welcome, <1>)
     * <p>
     * <p>
     * Reducer和Mapper中其实使用到了什么设计模式:模板
     */
    @Override
    protected void reduce(Object key, Iterable values, Context context) throws IOException, InterruptedException {
        int count = 0;
        Iterator<IntWritable> iterator = values.iterator();
        while (iterator.hasNext()) {
            IntWritable value = iterator.next();
            count += value.get();
        }
        context.write(key, new IntWritable(count));
    }
}

5.WordCountApp.java    // 连接hadoop 处理hadoop文件   进行 word count 统计

package com.imooc.bigdata.hadoop.mr.wc;


import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.Job;


import java.net.URI;

/**
 * 使用MR统计HDFS上的文件对应的词频
 * <p>
 * Driver: 配置Mapper,Reducer的相关属性
 * <p>
 * 提交到本地运行:开发过程中使用
 */
public class WordCountApp {
    public static void main(String[] args) throws Exception {
        System.setProperty("HADOOP_USER_NAME", "root"); // 绑定用户
        Configuration configuration = new Configuration(); // 初始化一个任务
        configuration.set("fs.defaultFS", "hdfs://192.168.107.216:8020");

        Job job = Job.getInstance(configuration);  // 创建一个Job
        job.setJarByClass(WordCountApp.class);  // 设置Job对应的参数: 主类
        job.setMapperClass(WordCountMapper.class); // 设置Job对应的参数: 设置自定义的Mapper处理类
        job.setReducerClass(WordCountReducer.class); // 设置Job对应的参数: 设置自定义的Reducer处理类
        job.setCombinerClass(WordCountReducer.class); // 添加Combiner的设置即可
        job.setMapOutputKeyClass(Text.class); // 设置Job对应的参数: Mapper输出key的类型
        job.setMapOutputValueClass(IntWritable.class);  // 设置Job对应的参数: Mapper输出value的类型
        job.setOutputKeyClass(Text.class); // 设置Job对应的参数: Reduce输出key的类型
        job.setOutputValueClass(IntWritable.class);  // 设置Job对应的参数: Reduce输出value的类型

        // 如果输出目录已经存在,则先删除
        FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.107.216:8020/"), configuration, "root");
        Path outputPath = new Path("/hdfsapi/output");
        if (fileSystem.exists(outputPath)) {
            fileSystem.delete(outputPath, true);
        }

        FileInputFormat.setInputPaths(job, new Path("/hdfsapi/test"));  // 设置Job对应的参数: Mapper输出key和value的类型:作业输入和输出的路径
        FileOutputFormat.setOutputPath(job, outputPath);  // 设置Job对应的参数: Mapper输出key和value的类型:作业输入和输出的路径
//    FileOutputFormat.setOutputPath(job, new Path("/hdfsapi/output"));  // 设置Job对应的参数: Mapper输出key和value的类型:作业输入和输出的路径

        boolean result = job.waitForCompletion(true); // 提交job
        System.exit(result ? 0 : -1);
    }
}

6.WordCountCombinerLocalApp.java    // 处理本地文件   进行 word count 统计

package com.imooc.bigdata.hadoop.mr.wc;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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 java.io.File;
import java.net.URI;

public class WordCountCombinerLocalApp {
  public static void main(String[] args) throws Exception {
    Configuration configuration = new Configuration();

    // 创建一个Job
    Job job = Job.getInstance(configuration);
    // 设置Job对应的参数: 主类
    job.setJarByClass(WordCountCombinerLocalApp.class);
    // 设置Job对应的参数: 设置自定义的Mapper和Reducer处理类
    job.setMapperClass(WordCountMapper.class);
    job.setReducerClass(WordCountReducer.class);
    // 添加Combiner的设置即可
    job.setCombinerClass(WordCountReducer.class);
    // 设置Job对应的参数: Mapper输出key和value的类型
    job.setMapOutputKeyClass(Text.class);
    job.setMapOutputValueClass(IntWritable.class);
    // 设置Job对应的参数: Reduce输出key和value的类型
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);

    FileUtilsDelete.deleteDirectory("output");  // // 如果输出目录已经存在,则先删除

    // 设置Job对应的参数: Mapper输出key和value的类型:作业输入和输出的路径
    FileInputFormat.setInputPaths(job, new Path("input"));
    FileOutputFormat.setOutputPath(job, new Path("output"));

    // 提交job
    boolean result = job.waitForCompletion(true);

    System.exit(result ? 0 : -1);

  }
}