1、背景
在实际项目中,输入数据往往是由许多小文件组成,这里的小文件是指小于HDFS系统Block大小的文件(默认128M), 然而每一个存储在HDFS中的文件、目录和块都映射为一个对象,存储在NameNode服务器内存中,通常占用150个字节。 如果有1千万个文件,就需要消耗大约3G的内存空间。如果是10亿个文件呢,简直不可想象。所以在项目开始前, 我们选择一种适合的方案来解决本项目的小文件问题
2、介绍
本地 D:\data目录下有 2012-09-17 至 2012-09-23 一共7天的数据集,我们需要将这7天的数据集按日期合并为7个大文件上传至 HDFS
3、数据
本地 D:\data目录下的所有数据,如下图所示:
4、分析
基于项目的需求,我们通过下面几个步骤完成
1、获取 D:\data目录下的所有日期路径,循环所有日期路径,通过globStatus()方法获取所有txt格式文件路径。
2、最后通过IOUtils.copyBytes(in, out, 4096, false)方法将数据集合并为大文件,并上传至 HDFS
5、实现
自定义RegexAcceptPathFilter类实现 PathFilter,比如只接受D:\data\2012-09-17日期目录下txt格式的文件
/**
* @ProjectName FileMerge
* @PackageName com.buaa
* @ClassName RegexAcceptPathFilter
* @Description 接受 regex 格式的文件
* @Date 2016-04-18 21:58:07
*/
public static class RegexAcceptPathFilter implements PathFilter {
private final String regex;
public RegexAcceptPathFilter(String regex) {
this.regex = regex;
}
@Override
public boolean accept(Path path) {
boolean flag = path.toString().matches(regex);
return flag;
}
}
实现主程序 merge 方法,完成数据集的合并,并上传至 HDFS
/**
* 合并
*
* @param srcPath 源目录
* @param destPath 目标目录
*/
public static void merge(String srcPath,String destPath) {
try{
// 读取hadoop文件系统的配置
Configuration conf = new Configuration();
// 获取远端文件系统
URI uri = new URI(HDFSUri);
FileSystem remote = FileSystem.get(uri, conf);
// 获得本地文件系统
FileSystem local = FileSystem.getLocal(conf);
// 获取data目录下的所有文件路径
Path[] dirs = FileUtil.stat2Paths(local.globStatus(new Path(srcPath)));
FSDataOutputStream out = null;
FSDataInputStream in = null;
for (Path dir : dirs) {
// 文件名称
String fileName = dir.getName().replace("-", "");
// 只接受目录下的.txt文件
FileStatus[] localStatus = local.globStatus(new Path(dir + "/*"), new RegexAcceptPathFilter("^.*.txt$"));
// 获得目录下的所有文件
Path[] listedPaths = FileUtil.stat2Paths(localStatus);
// 输出路径
Path block = new Path(destPath + "/" + fileName + ".txt");
// 打开输出流
out = remote.create(block);
for (Path p : listedPaths) {
// 打开输入流
in = local.open(p);
// 复制数据
IOUtils.copyBytes(in, out, 4096, false);
// 关闭输入流
in.close();
}
if (out != null) {
// 关闭输出流
out.close();
}
}
}catch(Exception e){
logger.error("", e);
}
}
6、一些运行代码
/**
* main方法
*
* @param args
*/
public static void main(String[] args) {
merge("D:\\data\\*","/buaa");
}
7、结果