Hdfsには2つのサブフォルダーがあるフォルダーがあり、各フォルダーには約30個のサブフォルダーがあり、それぞれにxmlファイルが含まれています。メインフォルダーのパスのみを提供するすべてのxmlファイルを一覧表示したい。ローカルで Apache commons-io's FileUtils.listFiles()でこれを行うことができます。私はこれを試しました
FileStatus[] status = fs.listStatus( new Path( args[ 0 ] ) );
ただし、最初の2つのサブフォルダーのみがリストされ、それ以上は進みません。 hadoopでこれを行う方法はありますか?
FileSystem オブジェクトを使用し、結果のFileStatusオブジェクトでいくつかのロジックを実行して、サブディレクトリに手動で再帰する必要があります。
listStatus(Path、PathFilter) メソッドを使用して、PathFilterを適用してxmlファイルのみを返すこともできます。
Hadoop FsShellクラスには、hadoop fs -lsrコマンドの例があります。これは、再帰lsです。 ソース の590行目を参照(再帰ステップは635行目でトリガーされます)
Hadoop 2. * APIを使用している場合、よりエレガントなソリューションがあります。
Configuration conf = getConf();
Job job = Job.getInstance(conf);
FileSystem fs = FileSystem.get(conf);
//the second boolean parameter here sets the recursion to true
RemoteIterator<LocatedFileStatus> fileStatusListIterator = fs.listFiles(
new Path("path/to/lib"), true);
while(fileStatusListIterator.hasNext()){
LocatedFileStatus fileStatus = fileStatusListIterator.next();
//do stuff with the file like ...
job.addFileToClassPath(fileStatus.getPath());
}
これを試しましたか:
import Java.io.*;
import Java.util.*;
import Java.net.*;
import org.Apache.hadoop.fs.*;
import org.Apache.hadoop.conf.*;
import org.Apache.hadoop.io.*;
import org.Apache.hadoop.mapred.*;
import org.Apache.hadoop.util.*;
public class cat{
public static void main (String [] args) throws Exception{
try{
FileSystem fs = FileSystem.get(new Configuration());
FileStatus[] status = fs.listStatus(new Path("hdfs://test.com:9000/user/test/in")); // you need to pass in your hdfs path
for (int i=0;i<status.length;i++){
BufferedReader br=new BufferedReader(new InputStreamReader(fs.open(status[i].getPath())));
String line;
line=br.readLine();
while (line != null){
System.out.println(line);
line=br.readLine();
}
}
}catch(Exception e){
System.out.println("File not found");
}
}
}
/**
* @param filePath
* @param fs
* @return list of absolute file path present in given path
* @throws FileNotFoundException
* @throws IOException
*/
public static List<String> getAllFilePath(Path filePath, FileSystem fs) throws FileNotFoundException, IOException {
List<String> fileList = new ArrayList<String>();
FileStatus[] fileStatus = fs.listStatus(filePath);
for (FileStatus fileStat : fileStatus) {
if (fileStat.isDirectory()) {
fileList.addAll(getAllFilePath(fileStat.getPath(), fs));
} else {
fileList.add(fileStat.getPath().toString());
}
}
return fileList;
}
簡単な例:次のファイル構造があるとします:
a -> b
-> c -> d
-> e
-> d -> f
上記のコードを使用すると、次のものが得られます。
a/b
a/c/d
a/c/e
a/d/f
リーフ(つまりfileNames)のみが必要な場合は、else
ブロックで次のコードを使用します。
...
} else {
String fileName = fileStat.getPath().toString();
fileList.add(fileName.substring(fileName.lastIndexOf("/") + 1));
}
これにより以下が得られます。
b
d
e
f
これで、Sparkを使用して、同じ方法で他のアプローチ(Hadoop MRなど)より高速に実行できます。コードスニペットを次に示します。
def traverseDirectory(filePath:String,recursiveTraverse:Boolean,filePaths:ListBuffer[String]) {
val files = FileSystem.get( sparkContext.hadoopConfiguration ).listStatus(new Path(filePath))
files.foreach { fileStatus => {
if(!fileStatus.isDirectory() && fileStatus.getPath().getName().endsWith(".xml")) {
filePaths+=fileStatus.getPath().toString()
}
else if(fileStatus.isDirectory()) {
traverseDirectory(fileStatus.getPath().toString(), recursiveTraverse, filePaths)
}
}
}
}
特定のHDFSディレクトリ内のファイルの数をカウントするコードスニペットを次に示します(これを使用して、特定のETLコードで使用するレデューサーの数を決定しました)。必要に応じてこれを簡単に変更できます。
private int calculateNumberOfReducers(String input) throws IOException {
int numberOfReducers = 0;
Path inputPath = new Path(input);
FileSystem fs = inputPath.getFileSystem(getConf());
FileStatus[] statuses = fs.globStatus(inputPath);
for(FileStatus status: statuses) {
if(status.isDirectory()) {
numberOfReducers += getNumberOfInputFiles(status, fs);
} else if(status.isFile()) {
numberOfReducers ++;
}
}
return numberOfReducers;
}
/**
* Recursively determines number of input files in an HDFS directory
*
* @param status instance of FileStatus
* @param fs instance of FileSystem
* @return number of input files within particular HDFS directory
* @throws IOException
*/
private int getNumberOfInputFiles(FileStatus status, FileSystem fs) throws IOException {
int inputFileCount = 0;
if(status.isDirectory()) {
FileStatus[] files = fs.listStatus(status.getPath());
for(FileStatus file: files) {
inputFileCount += getNumberOfInputFiles(file, fs);
}
} else {
inputFileCount ++;
}
return inputFileCount;
}
再帰的アプローチと非再帰的アプローチの両方のコードスニペット:
//helper method to get the list of files from the HDFS path
public static List<String>
listFilesFromHDFSPath(Configuration hadoopConfiguration,
String hdfsPath,
boolean recursive) throws IOException,
IllegalArgumentException
{
//resulting list of files
List<String> filePaths = new ArrayList<String>();
//get path from string and then the filesystem
Path path = new Path(hdfsPath); //throws IllegalArgumentException
FileSystem fs = path.getFileSystem(hadoopConfiguration);
//if recursive approach is requested
if(recursive)
{
//(heap issues with recursive approach) => using a queue
Queue<Path> fileQueue = new LinkedList<Path>();
//add the obtained path to the queue
fileQueue.add(path);
//while the fileQueue is not empty
while (!fileQueue.isEmpty())
{
//get the file path from queue
Path filePath = fileQueue.remove();
//filePath refers to a file
if (fs.isFile(filePath))
{
filePaths.add(filePath.toString());
}
else //else filePath refers to a directory
{
//list paths in the directory and add to the queue
FileStatus[] fileStatuses = fs.listStatus(filePath);
for (FileStatus fileStatus : fileStatuses)
{
fileQueue.add(fileStatus.getPath());
} // for
} // else
} // while
} // if
else //non-recursive approach => no heap overhead
{
//if the given hdfsPath is actually directory
if(fs.isDirectory(path))
{
FileStatus[] fileStatuses = fs.listStatus(path);
//loop all file statuses
for(FileStatus fileStatus : fileStatuses)
{
//if the given status is a file, then update the resulting list
if(fileStatus.isFile())
filePaths.add(fileStatus.getPath().toString());
} // for
} // if
else //it is a file then
{
//return the one and only file path to the resulting list
filePaths.add(path.toString());
} // else
} // else
//close filesystem; no more operations
fs.close();
//return the resulting list
return filePaths;
} // listFilesFromHDFSPath
再帰的アプローチを使用しない(ヒープの問題):)キューを使用する
queue.add(param_dir)
while (queue is not empty){
directory= queue.pop
- get items from current directory
- if item is file add to a list (final list)
- if item is directory => queue.Push
}
それは簡単でした、お楽しみください!
提案をしてくれたRadu Adrian Moldovanに感謝します。
キューを使用した実装は次のとおりです。
private static List<String> listAllFilePath(Path hdfsFilePath, FileSystem fs)
throws FileNotFoundException, IOException {
List<String> filePathList = new ArrayList<String>();
Queue<Path> fileQueue = new LinkedList<Path>();
fileQueue.add(hdfsFilePath);
while (!fileQueue.isEmpty()) {
Path filePath = fileQueue.remove();
if (fs.isFile(filePath)) {
filePathList.add(filePath.toString());
} else {
FileStatus[] fileStatus = fs.listStatus(filePath);
for (FileStatus fileStat : fileStatus) {
fileQueue.add(fileStat.getPath());
}
}
}
return filePathList;
}