@Test
public void urlHdfs() throws IOException {
//1:注册url
URL.setURLStreamHandlerFactory( new FsUrlStreamHandlerFactory());
//2:获取hdfs文件的输入流
InputStream inputStream = new URL("hdfs://node01:8020/a.txt").openStream();
//3:获取本地文件的输出流
FileOutputStream outputStream = new FileOutputStream(new File("D:\\hello2.txt"));
//4:实现文件的拷贝
IOUtils.copy(inputStream, outputStream);
//5:关流
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
在Java中操作HDFS,主要涉及以下class:
FileSystem fs = FileSystem.get(conf)
/*
获取FileSystem;方式1
*/
@Test
public void getFileSystem1() throws IOException {
//1:创建Configuration对象
Configuration configuration = new Configuration();
//2:设置文件系统的类型
configuration.set("fs.defaultFS", "hdfs://node01:8020");
//3:获取指定的文件系统
FileSystem fileSystem = FileSystem.get(configuration);
//4:输出
System.out.println(fileSystem);
}
/*
获取FileSystem;方式2
*/
@Test
public void getFileSystem2() throws URISyntaxException, IOException {
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
System.out.println(fileSystem);
}
/*
获取FileSystem;方式3
*/
@Test
public void getFileSystem3() throws IOException {
Configuration configuration = new Configuration();
//指定文件系统类型
configuration.set("fs.defaultFS", "hdfs://node01:8020");
//获取指定的文件系统
FileSystem fileSystem = FileSystem.newInstance(configuration);
System.out.println(fileSystem);
}
/*
获取FileSystem;方式4
*/
@Test
public void getFileSystem4() throws URISyntaxException, IOException {
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://node01:8020"), new Configuration());
System.out.println(fileSystem);
}
/*
hdfs文件的遍历
*/
@Test
public void listFiles() throws URISyntaxException, IOException {
//1:获取FileSystem实例
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
//2:调用方法listFiles 获取 /目录下所有的文件信息,,参数true代表递归遍历
RemoteIterator<LocatedFileStatus> iterator = fileSystem.listFiles(new Path("/"), true);
//3:遍历迭代器
while (iterator.hasNext()){
LocatedFileStatus fileStatus = iterator.next();
//获取文件的绝对路径 : hdfs://node01:8020/xxx
//getPath()方法就是获取绝对路径
System.out.println(fileStatus.getPath() + "----" +fileStatus.getPath().getName());
//文件的block信息
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
System.out.println("block数:"+blockLocations.length);
}
}
/*
hdfs创建文件夹
*/
@Test
public void mkdirsTest() throws URISyntaxException, IOException {
//1:获取FileSystem实例
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
//2:创建文件夹
//boolean bl = fileSystem.mkdirs(new Path("/aaa/bbb/ccc"));
//创建文件
fileSystem.create(new Path("/aaa/bbb/ccc/a.txt"));
//如果父目录不存在,则会一并创建
fileSystem.create(new Path("/aaa2/bbb/ccc/a.txt"));
//System.out.println(bl);
//3: 关闭FileSystem
//fileSystem.close();
}
/*
实现文件的下载
*/
@Test
public void downloadFile() throws URISyntaxException, IOException {
//1:获取FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
//2:获取hdfs的输入流,,注意获取的HDFS文件系统上的路径
FSDataInputStream inputStream = fileSystem.open(new Path("/a.txt"));
//3:获取本地路径的输出流,,注意这里获取的是本地磁盘路径
FileOutputStream outputStream = new FileOutputStream("D://a.txt");
//4:文件的拷贝
IOUtils.copy(inputStream, outputStream);
//5:关闭流
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
fileSystem.close();
}
通过对比方式1,可以使用copyToLocalFile方法一步到位。
/*
实现文件的下载:方式2
*/
@Test
public void downloadFile2() throws URISyntaxException, IOException, InterruptedException {
//1:获取FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(),"root");
//2:调用方法,实现文件的下载
fileSystem.copyToLocalFile(new Path("/a.txt"), new Path("D://a4.txt"));
//3:关闭FileSystem
fileSystem.close();
}
和文件下载格式一样,,不同的是方法不同,
文件上传使用copyFromLocalFile方法一步到位。
注意:
/*
文件的上传
*/
@Test
public void uploadFile() throws URISyntaxException, IOException {
//1:获取FileSystem
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
//2:调用方法,实现上传
fileSystem.copyFromLocalFile(new Path("D://set.xml"), new Path("/"));
//3:关闭FileSystem
fileSystem.close();
}
注意查看该文件的编辑内容, 默认为 false ,, 就是说让所有权限自身的作用失效,,也就是说在hdfs上显示的rwx权限没有任何意义。 所以要想修改文件的权限,就得修改为true
接下来我们验证上面参数的作用
首先通过命令来修改hdfs系统上的a.txt文件权限
hdfs dfs -chmod 000 /a.txt
然后我们通过Java Api 来下载该文件到本地磁盘D盘下
@Test
public void mkdirsTest() throws URISyntaxException, IOException {
//1:获取FileSystem实例
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.217.128:9000"), new Configuration());
//2:调用方法,实现文件的下载
fileSystem.copyToLocalFile(new Path("/a.txt"), new Path("D://a2.txt"));
//3:关闭FileSystem
fileSystem.close();
}
运行后发现在本地D盘下有a2.txt文件,
通过测试,发现即使该文件的权限被改为000,结果还是可以被访问,然后被下载到本地,,所以说,,,httpfs-site.xml文件中该部分参数为false的话,就是让所有权自身的作用失效。
注意:如果要将该参数改为true,则首先必须将hdfs集群关闭,不能在运行状态下修改配置文件。
下面我们继续进行验证,,这次先在Hadoop主目录下的sbin目录关闭hdfs文件系统,通过如下命令
sbin/stop-dfs.sh
注意:
这里如果将master主节点上的配置文件改为true的话,也必须修改其他节点的配置文件。
然后通过分发命令,进行分发:
scp hdfs-site.xml slave1:$PWD
scp hdfs-site.xml slave2:$PWD
sbin/start-all.sh
然后我们使用Java Api 再次进行下载文件,,会发生如下报错,,应为此时我们将配置文件中的参数修改为true,就是使得所有的文件权限生效。所以再次下载访问时,回报如下错误。
由于权限rwx作用全部生效,,所以我们如果还想通过Java Api进行下载的话,就必须得修改该文件的权限。 同时一定要注意该文件的所属用户!!!
hdfs dfs -chmod 666 /a.txt
如果想要不修改该文件的权限就下载访问该文件,就可以通过伪装用户的方式,来进行下载。
就是在Java代码中的get方法中多添加一个参数,该参数则是要伪装的用户名字。例如下面代码:
@Test
public void mkdirsTest() throws URISyntaxException, IOException, InterruptedException {
//1:获取FileSystem实例
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.217.128:9000"), new Configuration(),"root");
//2:调用方法,实现文件的下载
fileSystem.copyToLocalFile(new Path("/a.txt"), new Path("D://a4.txt"));
//3:关闭FileSystem
fileSystem.close();
}
由于Hadoop擅长储存大文件,因为大文件的元数据信息比较少,如果Hadoop集群中有大量的小文件,那么每个小文件都需要维护一份元数据信息,会大大的增加集群管理元数据的内存压力,所以在实际工作当中,如果有必要一定要将小文件合并成为大文件进行一起处理。
在我们的HDFS的Shell命令模式下,可以通过命令行将很多hdfs文件合并成一个大文件下载到本地。
例如我们将hdfs根目录下的xml文件合并成hello.xml文件并且下载到本地:
hdfs dfs -getmerge /*.xml ./hello.xml
如上图所示,本地hello.xml文件的内容则是core-site.xml文件和hdfs-site.xml文件内容的合并。
既然可以在下载的时候将这些小文件合并成一个大文件下载,那么就能在本地合并小文件进行上传。
/*
小文件的合并
*/
@Test
public void mergeFile() throws URISyntaxException, IOException, InterruptedException {
//1:获取FileSystem(分布式文件系统),,伪装用户root
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(),"root");
//2:获取hdfs大文件的输出流
FSDataOutputStream outputStream = fileSystem.create(new Path("/big_txt.txt"));
//3:获取一个本地文件系统
LocalFileSystem localFileSystem = FileSystem.getLocal(new Configuration());
//4:获取本地文件夹下所有文件的详情
FileStatus[] fileStatuses = localFileSystem.listStatus(new Path("D:\\input"));
//5:遍历每个文件,获取每个文件的输入流
for (FileStatus fileStatus : fileStatuses) {
FSDataInputStream inputStream = localFileSystem.open(fileStatus.getPath());
//6:将小文件的数据复制到大文件
IOUtils.copy(inputStream, outputStream);
IOUtils.closeQuietly(inputStream);
}
//7:关闭流
IOUtils.closeQuietly(outputStream);
localFileSystem.close();
fileSystem.close();
}