公司需求上传zip文件到hdfs中,然后解压缩。百度很久没找到直接在hadoop中解压zip的方法。大多数都是使用java连接hdfs读取、解压缩、写回hdfs中,但是这样逃不开网络带宽的限制。所以就想能不能直接在hadoop环境中解压zip,想起之前使用自定义udf函数,便想试试把Java读取解压结合到udf中,然后使用hive或者impala直接在hadoop环境下执行。
准备
新建一个空的maven项目。依赖如下。(有些不一定用上了,反正全ctrl +c了)
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.1.0</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.0</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>3.1.0</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
UDF核心代码
package com.zx;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hive.ql.exec.UDF;
import java.io.*;
public class Unzip extends UDF {
public String evaluate (String path) throws IOException {
String dir=path.split("\\.")[0]+"/";
Configuration conf=new Configuration();
FileSystem fs = FileSystem.get(conf);
ZipArchiveEntry zipEntry = null;
try(
// 读取hdfs上文件到输入流
FSDataInputStream fsInputStream = fs.open(new Path(path));
ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(fsInputStream);
){
// 判断是否能获取zipEntity对象
while ((zipEntry = zipInputStream.getNextZipEntry()) != null) {
// 获取当前解压的文件名
String entryName = zipEntry.getName();
// 判断解压文件是否非文件夹
if (!zipEntry.isDirectory()) {
try(
FSDataOutputStream fsDataOut = fs.create(new Path(dir + entryName));
){
// 在hdfs上创建指定文件
int byteSize = 10 * 1024 * 1024;
byte[] buffer = new byte[byteSize];
int nNumber;
// 并将解压的内容写入hdfs文件
while ((nNumber = zipInputStream.read(buffer, 0, byteSize)) != -1) {
fsDataOut.write(buffer, 0, nNumber);
}
fsDataOut.flush();
}
}
}
} catch (Exception e) {
return e.getMessage();
} finally {
}
return "success";
}
}
解释一下evaluate方法的参数是hdfs中一个zip文件路径,在该路径同级生成一个同名目录,将解压后的文件写进去。
打包上传使用
使用maven打jar包。将jar包上传到hdfs路径下,我是使用hue的webui上传后的路径为/targetFile/300Data.zip
上传完成后,在impala中创建函数
create function unzip(string) returns string location 'hdfs://node1/targetFile/unzip.jar' symbol='com.zx.Unzip';
函数创建成功后测试解压300Data.zip。在impala中查询
select unzip("/targetFile/300Data.zip")
查询成功,在targetFile下又300Data目录和目录下解压后的txt文件,解压完成。
在项目中直接使用impala执行该语句就行,比如我使用的是springboot配置好impala数据源,直接执行。
方法二
第一种函数对于压缩包比较大的情况下会超时。导致不能全部解压。
所以可以改造一下,添加一个main方法打jar包上传到hadoop所在linux中
public static void main(String[] args) throws IOException {
if(args==null||args.length==0)throw new RuntimeException("请输入要解压的zip文件路径");
int count=0;
long start=System.currentTimeMillis();
String path=args[0];
String dir=path.split("\\.")[0]+"/";
Configuration conf=new Configuration();
FileSystem fs = FileSystem.get(conf);
ZipArchiveEntry zipEntry = null;
try(
// 读取hdfs上文件到输入流
FSDataInputStream fsInputStream = fs.open(new Path(path));
ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(fsInputStream);
){
// 判断是否能获取zipEntity对象
while ((zipEntry = zipInputStream.getNextZipEntry()) != null) {
// 获取当前解压的文件名
String entryName = zipEntry.getName();
// 判断解压文件是否非文件夹
if (!zipEntry.isDirectory()) {
System.out.println("正在解压第 "+(++count)+" 个文件");
try(
FSDataOutputStream fsDataOut = fs.create(new Path(dir + entryName));
){
// 在hdfs上创建指定文件
int byteSize = 10 * 1024 * 1024;
byte[] buffer = new byte[byteSize];
int nNumber;
// 并将解压的内容写入hdfs文件
while ((nNumber = zipInputStream.read(buffer, 0, byteSize)) != -1) {
fsDataOut.write(buffer, 0, nNumber);
}
fsDataOut.flush();
}
}
}
long t=(System.currentTimeMillis()-start)/1000;
System.out.println("全部解压完成共 "+count+" 个文件,共用时 "+t+" 秒");
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
我是上传到/root下通过下方命令执行
hadoop jar hadoopUnzip.jar /targetFile/da8017b3a5574c1cb0cdabbd23273e16.zip
结果如下:
[root@node1 ~]# hadoop jar hadoopUnzip.jar /targetFile/da8017b3a5574c1cb0cdabbd23273e16.zip
WARNING: Use "yarn jar" to launch YARN applications.
正在解压第 1 个文件
正在解压第 2 个文件
正在解压第 3 个文件
正在解压第 4 个文件
正在解压第 5 个文件
正在解压第 6 个文件
正在解压第 7 个文件
正在解压第 8 个文件
正在解压第 9 个文件
正在解压第 10 个文件
正在解压第 11 个文件
正在解压第 12 个文件
正在解压第 13 个文件
正在解压第 14 个文件
正在解压第 15 个文件