HDFS之JAVA API學(xué)習(xí)筆記

本文是對(duì)HDFS的JAVA API操作的一個(gè)學(xué)習(xí)總結(jié),包括如下章節(jié)的內(nèi)容:

  • 概述
  • 目錄和文件操作
  • 文件上傳和下載
  • 讀寫(xiě)數(shù)據(jù)操作
  • 本地文件系統(tǒng)支持

參考資料:

1卿捎、本文介紹的內(nèi)容依賴hadoop環(huán)境仲智,關(guān)于hadoop運(yùn)行環(huán)境的搭建可參見(jiàn)《Hadoop運(yùn)行環(huán)境搭建》娘汞。

2烈钞、如果想了解下HDFS的基本概念漫仆,可先閱讀《HDFS學(xué)習(xí)筆記》捎拯。

一、概述

我們除了通過(guò)命令行接口訪問(wèn)HDFS系統(tǒng)外盲厌,還可以通過(guò)hadoop類庫(kù)提供的Java API編寫(xiě)java程序來(lái)訪問(wèn)HDFS系統(tǒng)署照,如進(jìn)行文件的上傳、下載吗浩、目錄的創(chuàng)建建芙、文件的刪除等各種文件操作。

hadoop類庫(kù)中提供的HDFS操作的核心API是FileSystem抽象類懂扼,該類提供了一系列方法來(lái)進(jìn)行相關(guān)的操作禁荸。

FileSystem是一個(gè)通用的文件系統(tǒng)API右蒲,它是一個(gè)抽象類,可以通過(guò)其提供的靜態(tài)工廠方法來(lái)獲取該類的實(shí)例赶熟。獲取HDFS文件系統(tǒng)FileSystem的實(shí)例最常用的靜態(tài)方法是:

static FileSystem get(Configuration conf);

參數(shù)Configuration對(duì)象是對(duì)文件系統(tǒng)中屬性信息的封裝瑰妄,默認(rèn)的屬性來(lái)自hadoop配置文件core-site.xml中的配置,當(dāng)然也可以通過(guò)代碼進(jìn)行屬性的設(shè)置映砖。進(jìn)行文件操作的基本流程是:

1间坐、創(chuàng)建Configuration對(duì)象

2、利用FileSystem 的靜態(tài)get方法獲取FileSystem 實(shí)例

3邑退、調(diào)用FileSystem 的方法進(jìn)行實(shí)際的文件操作

下面我們通過(guò)實(shí)際的例子來(lái)進(jìn)行學(xué)習(xí)竹宋。

二、目錄和文件操作API

1地技、創(chuàng)建目錄

利用FileSystem的mkdirs方法可以創(chuàng)建文件或目錄蜈七,其定義如下:

public boolean mkdirs(Path f) throws IOException;

該方法的參數(shù)用于指定待創(chuàng)建的目錄路徑,需要說(shuō)明的是乓土,mkdirs命令可以級(jí)聯(lián)創(chuàng)建目錄宪潮,即如果指定待創(chuàng)建的目錄的上級(jí)目錄不存在溯警,則會(huì)一次幫創(chuàng)建趣苏。

例子代碼如下:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class CreateDir {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        try {
            FileSystem HDFS = FileSystem.get(conf);
HDFS.mkdirs(new Path(args[0]));
            System.out.println("cerate success");
        } catch (IOException e) {
            System.out.println("cerate error");
            e.printStackTrace();
        } 
    }
}

上面代碼是一個(gè)普通的帶main方法的java類。main方法中第二行代碼如下:

conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");

上面代碼的目的是設(shè)置本程序(也就是HDFS客戶端)所訪問(wèn)的HDFS服務(wù)的地址梯轻。如果不通過(guò)代碼進(jìn)行設(shè)置食磕,則默認(rèn)會(huì)取hadoop安裝環(huán)境下core-site.xml文件中配置的"fs.defaultFS"的屬性值。所以喳挑,如果程序是運(yùn)行在服務(wù)器上(即name節(jié)點(diǎn)所在機(jī)器)則不用通過(guò)代碼進(jìn)行顯示設(shè)置彬伦。

代碼有了,那我們?cè)趺淳幾g和執(zhí)行程序呢伊诵?這個(gè)和處理mapreduce程序類似单绑,可以參考《mapreduce學(xué)習(xí)筆記》一文中的介紹。這里再簡(jiǎn)單介紹下曹宴。

我們?cè)贗DE中編寫(xiě)上面程序搂橙,要想代碼可以編譯成功,只需要引入hadoop-common-2.7.6.jar笛坦。但運(yùn)行的時(shí)候区转,需要依賴更多的Jar包。最簡(jiǎn)單的運(yùn)行方式是版扩,將程序編譯后的class打成jar包废离,然后在命令行下利用hadoop jar命令來(lái)執(zhí)行,該命令會(huì)自動(dòng)引入執(zhí)行程序需要的jar包礁芦。

假設(shè)上面代碼打成jar包的名稱為testhdfs.jar蜻韭,則在命令行下運(yùn)行如下命令(待創(chuàng)建的目錄路徑是通過(guò)參數(shù)傳入):

hadoop jar testhdfs.jar com.hdfs.CreateDir /testdir

執(zhí)行后,根目錄下就會(huì)生成一個(gè)testdir目錄。

下面介紹的例子可以利用同樣的方法進(jìn)行編譯和運(yùn)行湘捎,后續(xù)不會(huì)再重復(fù)介紹诀豁。

2、刪除目錄或文件

利用FileSystem的delete方法可以刪除文件或目錄窥妇,其定義如下:

public boolean delete(Path f, boolean recursive) throws IOException

如果參數(shù)f指定的是文件或空目錄舷胜,則參數(shù)recursive的值被忽略;

如果參數(shù)f指定的目錄不未空活翩,則如果參數(shù)recursive的值為false烹骨,則執(zhí)行會(huì)拋異常,只有值為true材泄,才會(huì)將該目錄及其下內(nèi)容全部刪除沮焕。

例子代碼如下:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class DeleteFile {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        try {
            FileSystem HDFS = FileSystem.get(conf);
            boolean re = HDFS.delete(new Path(args[0]), true);
            System.out.println("delete:"+re);
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

3、重命名(移動(dòng))文件或目錄

利用FileSystem的rename方法可以重命名文件或目錄拉宗,如果被移動(dòng)的文件或目錄其父目錄發(fā)生變化峦树,則相當(dāng)于移動(dòng)文件或目錄。該方法定義如下:

public boolean rename(Path src,Path dest) throws IOException

該方法有兩個(gè)參數(shù)旦事,第一個(gè)參數(shù)是被操作的文件或目錄的路徑魁巩,第二參數(shù)是待修改后的路徑。如果操作成功姐浮,方法的返回值為true,否則為false谷遂。

例子代碼如下:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class ReName {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        try {
            FileSystem HDFS = FileSystem.get(conf);
            boolean re = HDFS.rename(new Path(args[0]), new Path(args[1]));
            System.out.println("rename:"+re);
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

4、判斷文件或目錄是否存在

利用FileSystem的exists方法可以判斷指定的文件或目錄是否存在卖鲤,其定義如下:

public boolean exists(Path f) throws IOException;

如果存在肾扰,方法返回值為true,否則為false蛋逾。

例子代碼如下:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class CheckExist {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        try {
            FileSystem HDFS = FileSystem.get(conf);
            boolean re = HDFS.exists(new Path(args[0]));
            System.out.println("is exist:"+re);
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

5集晚、判斷是文件還是目錄

利用FileSystem的isFile方法可以判斷指定的路徑是不是文件;利用isDirectory方法可以判斷指定的路徑是不是目錄区匣。兩個(gè)方法的定義如下:

public boolean isFile(Path f) throws IOException;

public boolean isDirectory(Path f) throws IOException;

如果存在指定的路徑不存在偷拔,則上面兩個(gè)方法都返回false。

例子代碼如下:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class IsFile {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        try {
            FileSystem HDFS = FileSystem.get(conf);
            boolean re = HDFS.isDirectory(new Path(args[0]));
            System.out.println("isDirectory:"+re);
            re = HDFS.isFile(new Path(args[0]));
            System.out.println("isFile:"+re);
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

6沉颂、查看目錄和文件的內(nèi)容

利用FileSystem的listStatus方法可以獲取指定的文件的信息或目錄下內(nèi)容的信息条摸,其定義如下:

public FileStatus[] listStatus(Path arg0)

throws FileNotFoundException, IOException

該方法返回一個(gè)FileStatus類型的對(duì)象數(shù)組,F(xiàn)ileStatus對(duì)象中封裝了文件(或目錄的)各種信息铸屉,如名稱钉蒲,訪問(wèn)時(shí)間,大小等彻坛。方法的參數(shù)是一個(gè)路徑顷啼,如果路徑指向一個(gè)文件踏枣,則返回的是數(shù)組就一個(gè)元素,代表該文件的信息钙蒙;如果路徑指向的是是一個(gè)目錄茵瀑,則返回的是該目錄下子目錄和文件信息。

例子代碼如下:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class ShowFile {
    public static void main(String[] args) throws IOException {
        Configuration conf = null;
        FileSystem fs = null;
        conf = new Configuration();
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        fs = FileSystem.get(conf);
        FileStatus[] listStatus = fs.listStatus(new Path(args[0]));
        for(FileStatus fileStatus:listStatus){
            String fileName = fileStatus.getPath().getName();
            String type = fileStatus.isFile()?"file":"dir";
            long size = fileStatus.getLen();
            System.out.println(fileName+","+type+","+size);
        }
    }
}

7躬厌、復(fù)制文件或目錄

上面的例子都是利用FileSystem中的方法進(jìn)行文件和目錄的操作马昨,在文件操作中,還有一個(gè)常見(jiàn)的操作就是文件和目錄的復(fù)制扛施。但是FileSystem沒(méi)有提供接口來(lái)進(jìn)行復(fù)制操作鸿捧。這時(shí)可以利用FileContext接口提供的操作來(lái)實(shí)現(xiàn)。具體我們看一個(gè)例子疙渣,代碼如下:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.FileContext.Util;
import org.apache.hadoop.fs.Path;

public class CopyFile {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        try {
            FileContext fileContext = FileContext.getFileContext(conf);
            Util util = fileContext.util();
            boolean re=util.copy(new Path(args[0]), new Path(args[1]));
            System.out.println("copy:"+re);
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

上面代碼先利用FileContext 的靜態(tài)方法獲取到一個(gè)FileContext 對(duì)象匙奴,類似獲取FileSystem方法,需要傳入一個(gè)Configuration 對(duì)象的參數(shù)妄荔。然后再獲取一個(gè)Util對(duì)象泼菌。Util對(duì)象提供的copy方法可以完成復(fù)制操作。copy方法有兩個(gè)參數(shù)啦租,第1個(gè)參數(shù)是待復(fù)制的文件或目錄哗伯,第2個(gè)參數(shù)是復(fù)制后的路徑(含文件名或目錄名),要求文件或目錄名不存在刷钢。如果復(fù)制成功笋颤,copy方法返回true乳附。

Util對(duì)象中也有些方法内地,類似FileSystem中的方法可以完成相同功能的一些文件操作。

三赋除、文件上傳和下載操作API

1阱缓、上傳本地文件

利用FileSystem的copyFromLocalFile方法可以將本地文件或目錄(及目錄下所有內(nèi)容)上傳到HDFS系統(tǒng)上,其定義如下:

public void copyFromLocalFile(Path src, Path dst) throws IOException;

1)該方法有兩個(gè)參數(shù)举农,第一個(gè)參數(shù)是待上傳的文件或目錄的本地路徑荆针,第二個(gè)參數(shù)是HDFS系統(tǒng)上的目的路徑。

2)如果待上傳的是一個(gè)目錄颁糟,則會(huì)把整個(gè)目錄及目錄下的所有內(nèi)容上傳航背。

3)如果目的路徑是一個(gè)目錄,則會(huì)把本地文和目錄上傳到該目的目錄下棱貌,被上傳的文件或目錄名不變玖媚。如果目的路徑是一個(gè)不存在的路徑,則會(huì)把本地文和目錄上傳到該目的路徑的上級(jí)目錄下婚脱,被上傳的文件或目錄名變?yōu)槟康穆窂街付ǖ拿Q今魔。

4)copyFromLocalFile還有多個(gè)重載方法勺像,可以選擇上傳后是否同時(shí)刪除本地文件或目錄,以及是否覆蓋目的文件或目錄错森。

例子代碼如下:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class PutFile {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        try {
            FileSystem HDFS = FileSystem.get(conf);
            HDFS.copyFromLocalFile(new Path(args[0]), new Path(args[1]));
            System.out.println("action success");
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

2吟宦、下載文件到本地

利用FileSystem的copyToLocalFile方法可以將HDFS系統(tǒng)上的文件或目錄(及目錄下所有內(nèi)容)下載到本地系統(tǒng)上,其定義如下:

public void copyToLocalFile(Path src, Path dst) throws IOException;

該方法的使用與上面介紹的上傳本地文件或目錄到HDFS系統(tǒng)上的copyFromLocalFile方法非常類似涩维,區(qū)別只是源路徑和目的路徑正好相反殃姓,這里不再詳細(xì)介紹。直接看一個(gè)例子:

package com.hdfs;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class PutFile {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        try {
            FileSystem HDFS = FileSystem.get(conf);
            HDFS.copyToLocalFile(new Path(args[0]), new Path(args[1]));
            System.out.println("action success");
        } catch (IOException e) {
            e.printStackTrace();
        } 
    }
}

四瓦阐、讀寫(xiě)數(shù)據(jù)操作API

1辰狡、讀取數(shù)據(jù)

調(diào)用FileSystem的open方法(參數(shù)是HDFS系統(tǒng)上的一個(gè)文件)會(huì)返回一個(gè)FSDataInputStream對(duì)象,利用該對(duì)象可以讀取文件中的數(shù)據(jù)垄分。

我們先看一個(gè)例子代碼宛篇,該例子是讀取一個(gè)文本文件中的內(nèi)容:

package com.hdfs;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class ReadData {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        BufferedReader reader = null;
        String line = null;
        try {
            FileSystem HDFS = FileSystem.get(conf);
            FSDataInputStream in = HDFS.open(new Path(args[0]));
            reader = new BufferedReader(new InputStreamReader(in));
            while ((line = reader.readLine()) != null){
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(reader!=null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上面程序,首先調(diào)用FileSystem的open方法獲取到一個(gè)FSDataInputStream對(duì)象薄湿,是一個(gè)數(shù)據(jù)流對(duì)象叫倍,通過(guò)它提供的方法可以讀取流中的數(shù)據(jù),但該類的方法都是一些級(jí)別較低的底層方法豺瘤,不適合用來(lái)讀取文本文件吆倦,對(duì)于文本文件,我們一般習(xí)慣逐行讀取數(shù)據(jù)坐求,所以使用了JAVA IO 中的BufferedReader來(lái)包裝FSDataInputStream對(duì)象來(lái)讀取數(shù)據(jù)蚕泽。最后要在finally語(yǔ)句塊中關(guān)閉BufferedReader對(duì)象。

如果要讀取二進(jìn)制文件桥嗤,可利用工具類IOUtils中的方法须妻,如下面例子:

package com.hdfs;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

public class ReadData2 {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        FSDataInputStream in = null;
        try {
            FileSystem HDFS = FileSystem.get(conf);
            in = HDFS.open(new Path(args[0]));
            OutputStream out = System.out;
            IOUtils.copyBytes(in, out, 4096, true);
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            IOUtils.closeStream(in);
        }
    }
}

上面程序使用的IOUtils的copyBytes方法(它有多個(gè)重載的方法),該方法是個(gè)通用的方法泛领,用于將一個(gè)數(shù)據(jù)流中的數(shù)據(jù)寫(xiě)入另外一個(gè)數(shù)據(jù)流荒吏,它有4個(gè)參數(shù),第1個(gè)參數(shù)是InputStream類型渊鞋,即被讀取出來(lái)的數(shù)據(jù)流绰更,在這個(gè)例子中就是FSDataInputStream 對(duì)象,第2個(gè)參數(shù)是OutputStream類型锡宋,即要寫(xiě)入的數(shù)據(jù)流對(duì)象儡湾,這里為了演示簡(jiǎn)單,直接使用了系統(tǒng)輸出對(duì)象System.out,第3個(gè)參數(shù)表示本次讀寫(xiě)操作數(shù)據(jù)的規(guī)模(這里是4096個(gè)字節(jié))执俩,第4個(gè)參數(shù)是個(gè)布爾值徐钠,為true,表示調(diào)用完畢后關(guān)閉流對(duì)象奠滑。

需要說(shuō)明的是丹皱,如果第4個(gè)參數(shù)設(shè)置為true妒穴,則因?yàn)檎{(diào)用后文件流被關(guān)閉了,不能再次讀取摊崭。如果要能連續(xù)多次調(diào)用IOUtils的copyBytes方法讼油,則第4個(gè)參數(shù)不能設(shè)置為true,以為在finally語(yǔ)句塊中也進(jìn)行了關(guān)閉操作呢簸,所以不會(huì)導(dǎo)致文件流不會(huì)被關(guān)閉矮台。FSDataInputStream 對(duì)象內(nèi)部有一個(gè)位置指針,被讀取一次后根时,指針會(huì)指向讀取數(shù)據(jù)后的位置瘦赫,再次讀取,會(huì)從新的位置往后讀取蛤迎。

同時(shí)FSDataInputStream 對(duì)象支持隨機(jī)訪問(wèn)确虱,可以調(diào)用其seek方法定位到文件指定的位置開(kāi)始讀取,注意不能超過(guò)文件的長(zhǎng)度替裆。

我們看下面一個(gè)例子校辩,利用seek方法多次讀取數(shù)據(jù),代碼如下:

package com.hdfs;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

public class ReadData2 {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        FSDataInputStream in = null;
        try {
            FileSystem HDFS = FileSystem.get(conf);
            in = HDFS.open(new Path(args[0]));
            OutputStream out = System.out;
            IOUtils.copyBytes(in, out, 4096,false);
            System.out.println("read again");
            in.seek(0);
            IOUtils.copyBytes(in, out, 4096, false);
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            IOUtils.closeStream(in);
        }
    }
}

需要注意的是辆童,seek方法是相對(duì)高開(kāi)銷的操作宜咒,需要慎重使用。對(duì)于大數(shù)據(jù)把鉴,建議采用順序讀取的方式(可采用Mapreduce來(lái)進(jìn)行讀取數(shù)據(jù))故黑。

2、創(chuàng)建文件寫(xiě)入數(shù)據(jù)

調(diào)用FileSystem的create方法(該方法有多個(gè)重載方法)會(huì)創(chuàng)建一個(gè)空的文件庭砍,同時(shí)該方法會(huì)返回一個(gè)FSDataOutputStream對(duì)象场晶,利用該對(duì)象可以往文件中寫(xiě)入數(shù)據(jù)。如果create方法指定的新文件所在的上級(jí)目錄不存在逗威,會(huì)自動(dòng)幫創(chuàng)建峰搪。

下面我們先看一個(gè)例子岔冀,該例子創(chuàng)建一個(gè)文件凯旭,寫(xiě)入文本信息:

package com.hdfs;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class WriteData {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        BufferedWriter writer = null;
        try {
            FileSystem HDFS = FileSystem.get(conf);
            FSDataOutputStream out = HDFS.create(new Path(args[0]));
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write("hello1");
            writer.newLine();
            writer.write("hello2");
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(writer!=null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上面代碼先調(diào)用FileSystem的create方法創(chuàng)建一個(gè)空文件(如果文件已存在,則會(huì)被覆蓋)使套,該方法返回一個(gè)FSDataOutputStream 對(duì)象罐呼,F(xiàn)SDataOutputStream 對(duì)象中有很多寫(xiě)數(shù)據(jù)的方法,不過(guò)直接使用不太方便侦高。我們這里是準(zhǔn)備下寫(xiě)入文本信息嫉柴,因此利用了java io中的 BufferedWriter 來(lái)寫(xiě)入文本信息。

如果要寫(xiě)入二進(jìn)制數(shù)據(jù)奉呛,可利用工具類IOUtils中的方法计螺,如下面例子:

package com.hdfs;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

public class WriteData2 {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        FSDataOutputStream out=null;
        try {
            FileSystem HDFS = FileSystem.get(conf);
            out = HDFS.create(new Path(args[0]));
            InputStream in = buildInputBuffer();
            IOUtils.copyBytes(in , out, 4096, true);
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            IOUtils.closeStream(out);
        }
    }

    private static InputStream buildInputBuffer() {
        byte[] byt = new byte[1024];
        for(int i=0;i<1024;i++){
            byt[i]=66;
        }
        InputStream in = new ByteArrayInputStream(byt);
        return in;
    }
}

上面程序使用的IOUtils的copyBytes方法與上面從HDFS文件中讀取數(shù)據(jù)例子中使用的是一樣的方法夯尽。只是我們這里是從一個(gè)字節(jié)流(為了簡(jiǎn)化,自己構(gòu)建的)中讀取數(shù)據(jù)寫(xiě)入到create方法返回的FSDataOutputStream 對(duì)象中登馒。

一般來(lái)說(shuō)匙握,往HDFS中寫(xiě)入數(shù)據(jù)都是數(shù)據(jù)量比較大的,整個(gè)過(guò)程所需時(shí)間較長(zhǎng)陈轿。為了讓客戶端能獲取寫(xiě)入過(guò)程中的狀態(tài)圈纺,F(xiàn)ileSystem的create另一個(gè)重載方法提供了一個(gè)參數(shù)(Progressable接口)可以獲取數(shù)據(jù)寫(xiě)入的進(jìn)度。具體我們看一個(gè)例子麦射,例子代碼如下:

package com.hdfs;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;

public class WriteData3 {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        FSDataOutputStream out=null;
        try {
            FileSystem HDFS = FileSystem.get(conf);
            out = HDFS.create(new Path(args[0]),new Progressable() {
                @Override
                public void progress() {
                    System.out.print(".");
                }
            });
            InputStream in = buildInputBuffer();
            IOUtils.copyBytes(in , out, 4096, true);
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            IOUtils.closeStream(out);
        }
    }

    private static InputStream buildInputBuffer() {
        int size = 1024*10000;
        byte[] byt = new byte[size];
        for(int i=0;i<size;i++){    
            byt[i]=66;
        }
        InputStream in = new ByteArrayInputStream(byt);
        return in;
    }
}

相比前面的例子蛾娶,在本例子中,FileSystem的create方法多了一個(gè)參數(shù),是一個(gè)Progressable接口對(duì)象潜秋,該接口有一個(gè)progress方法蛔琅,HDFS在寫(xiě)入數(shù)據(jù)的過(guò)程中,會(huì)根據(jù)進(jìn)度不斷調(diào)用progress方法峻呛,這樣我們實(shí)現(xiàn)progress方法就能知道數(shù)據(jù)寫(xiě)入的進(jìn)度了揍愁。

3、追加數(shù)據(jù)到已有的文件中

在某些場(chǎng)景下杀饵,我們需要往已經(jīng)存在文件的末尾追加寫(xiě)入數(shù)據(jù)莽囤。這時(shí)我們可以調(diào)用FileSystem的append方法來(lái)打開(kāi)一個(gè)文件,并返回FSDataOutputStream對(duì)象切距,然后就可以寫(xiě)入數(shù)據(jù)朽缎。除了使用append方法替換create方法,其它操作都一樣谜悟,這里不再重復(fù)介紹话肖。可以看一個(gè)簡(jiǎn)單例子葡幸,代碼如下:

package com.hdfs;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class AppendData {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");
        BufferedWriter writer = null;
        try {
            FileSystem HDFS = FileSystem.get(conf);
            FSDataOutputStream out = HDFS.append(new Path(args[0]));
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write("newdata");
            writer.newLine();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(writer!=null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

可以看出最筒,往已存在文件中添加數(shù)據(jù),只需將create方法換成append方法蔚叨。需要注意的是床蜘,append方法指定的文件必須已經(jīng)存在,如果不存在蔑水,h不會(huì)自動(dòng)創(chuàng)建邢锯,而是會(huì)報(bào)異常。

五搀别、本地文件系統(tǒng)支持

在本文中丹擎,我們通過(guò)多個(gè)例子介紹了如何使用hadoop提供的JAVA API來(lái)操作HDFS系統(tǒng)。可以看出蒂培,核心就是FileSystem類中提供的各種方法的使用再愈。在最上面我們也提到,F(xiàn)ileSystem類是一個(gè)通用的文件系統(tǒng)操作API护戳,不僅可以用來(lái)操作HDFS系統(tǒng)践磅,也可以用來(lái)訪問(wèn)其它文件系統(tǒng),比如本地文件系統(tǒng)灸异。

這就帶來(lái)一個(gè)好處府适,我們?cè)诩涵h(huán)境下開(kāi)發(fā)和調(diào)試代碼比較麻煩,這樣我們可以先訪問(wèn)本地的文件系統(tǒng)來(lái)進(jìn)行代碼邏輯的驗(yàn)證肺樟,如果代碼沒(méi)問(wèn)題檐春,我們可以再到集群環(huán)境下去驗(yàn)證。這有點(diǎn)和mapreduce程序也可以在本地運(yùn)行類似么伯。

我們把上面寫(xiě)數(shù)據(jù)的例子改成到本地環(huán)境下運(yùn)行疟暖,代碼如下:

package com.hdfs;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

public class WriteData {
    public static void main(String[] args) {
        Configuration conf=new Configuration(); 
        conf.set("fs.defaultFS", "file:///");
        BufferedWriter writer = null;
        try {
            FileSystem HDFS = FileSystem.get(conf);
            FSDataOutputStream out = HDFS.create(new Path(args[0]));
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write("hello1");
            writer.newLine();
            writer.write("hello2");
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(writer!=null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

可以看出,與前面例子相比田柔,我們只是改了一句代碼俐巴,將

conf.set("fs.defaultFS", "hdfs://192.168.1.3:8020");

語(yǔ)句改為

conf.set("fs.defaultFS", "file:///");

設(shè)置"fs.defaultFS"屬性為"file:///",意思就是訪問(wèn)的是本地文件系統(tǒng)硬爆。上面程序無(wú)論是在linux或海上windows下都可以運(yùn)行欣舵。如在linux下運(yùn)行:

hadoop jar testhdfs.jar /home/hadoop/test.txt

在windows下運(yùn)行:

hadoop jar testhdfs.jar d:/test.txt

需要特別注意的是,F(xiàn)ileSystem中的方法并不是全部能夠在各種文件系統(tǒng)中運(yùn)行缀磕,比如append方法經(jīng)過(guò)測(cè)試發(fā)現(xiàn)本地文件系統(tǒng)就不支持缘圈。所以在開(kāi)發(fā)調(diào)試時(shí)需要特別注意這點(diǎn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末袜蚕,一起剝皮案震驚了整個(gè)濱河市糟把,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌牲剃,老刑警劉巖遣疯,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凿傅,居然都是意外死亡缠犀,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)狭归,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)夭坪,“玉大人,你說(shuō)我怎么就攤上這事过椎。” “怎么了戏仓?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵疚宇,是天一觀的道長(zhǎng)亡鼠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)敷待,這世上最難降的妖魔是什么间涵? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮榜揖,結(jié)果婚禮上勾哩,老公的妹妹穿的比我還像新娘。我一直安慰自己举哟,他們只是感情好思劳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著妨猩,像睡著了一般潜叛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上壶硅,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天威兜,我揣著相機(jī)與錄音,去河邊找鬼庐椒。 笑死椒舵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的约谈。 我是一名探鬼主播逮栅,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼窗宇!你這毒婦竟也來(lái)了措伐?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤军俊,失蹤者是張志新(化名)和其女友劉穎侥加,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體粪躬,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡担败,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镰官。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片提前。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖泳唠,靈堂內(nèi)的尸體忽然破棺而出狈网,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布拓哺,位于F島的核電站勇垛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏士鸥。R本人自食惡果不足惜闲孤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烤礁。 院中可真熱鬧讼积,春花似錦、人聲如沸脚仔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)玻侥。三九已至决摧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凑兰,已是汗流浹背掌桩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留姑食,地道東北人波岛。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像音半,于是被迫代替她去往敵國(guó)和親则拷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 當(dāng)數(shù)據(jù)量增大到超出了單個(gè)物理計(jì)算機(jī)存儲(chǔ)容量時(shí),有必要把它分開(kāi)存儲(chǔ)在多個(gè)不同的計(jì)算機(jī)中。那些管理存儲(chǔ)在多個(gè)網(wǎng)絡(luò)互連的...
    單行線的旋律閱讀 1,920評(píng)論 0 7
  • 通過(guò)API操作HDFS 今天的主要內(nèi)容 HDFS獲取文件系統(tǒng) HDFS文件上傳 HDFS文件下載 HDFS目錄創(chuàng)建...
    須臾之北閱讀 2,705評(píng)論 0 3
  • 何德修來(lái)局蚀?累世緣生,如此道津坛善! 記石門(mén)初識(shí),依然若夢(mèng)邻眷;① 龍城期諾眠屎,依舊牽魂。② 動(dòng)魄平遙肆饶,縈情鄗地改衩,騃雨癡云為汝...
    彬彬有黎閱讀 323評(píng)論 4 4
  • 望盡浮生利欲囚,夙興難寐惱煩憂驯镊。 大日未從天際淡葫督, 休怨竭鞍,卷衣扶袖入人流。 待至玉樓重審視候衍, 羞問(wèn)笼蛛,舊時(shí)何故與書(shū)仇...
    墨白樂(lè)兮閱讀 229評(píng)論 1 12
  • 第三章中主要講述了一個(gè)主題洒放,即自控力是有限的蛉鹿,在這個(gè)方面的行為得到了控制,與此同時(shí)往湿,另外方面的行為往往可能...
    小柚子_488f閱讀 457評(píng)論 6 2